Skip to content

feat: endpoint to group spans by trace_id and session_id - #272

Merged
BrianNewsom merged 3 commits into
mainfrom
brnewsom/fp-203-add-group_by-support-for-span-queries-to-sessions
Jun 11, 2026
Merged

feat: endpoint to group spans by trace_id and session_id#272
BrianNewsom merged 3 commits into
mainfrom
brnewsom/fp-203-add-group_by-support-for-span-queries-to-sessions

Conversation

@BrianNewsom

@BrianNewsom BrianNewsom commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features
    • New API endpoint to list and aggregate spans grouped by fields (e.g., trace or session)
    • CLI command to query and manage span groups with filtering, pagination, and sorting
  • Documentation
    • Intake service docs updated to include the new grouping endpoint
  • Tests
    • Added unit and integration tests covering grouping behavior and responses
  • Chores
    • Added endpoint permission mapping for the new API

Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
@BrianNewsom
BrianNewsom requested review from a team as code owners June 10, 2026 23:01
@github-actions github-actions Bot added the feat label Jun 10, 2026
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 3ef341ed-14e7-4a2a-ae1a-d9d306d8c764

📥 Commits

Reviewing files that changed from the base of the PR and between f9dbffe and 1be2704.

⛔ Files ignored due to path filters (3)
  • sdk/python/nemo-platform/.nmpcontext/openapi.yaml is excluded by !sdk/**
  • sdk/python/nemo-platform/.nmpcontext/stainless.yaml is excluded by !sdk/**
  • sdk/stainless.yaml is excluded by !sdk/**
📒 Files selected for processing (3)
  • openapi/ga/individual/platform.openapi.yaml
  • openapi/ga/openapi.yaml
  • openapi/openapi.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • openapi/openapi.yaml

📝 Walkthrough

Walkthrough

Adds GET /apis/intake/v2/workspaces/{workspace}/spans/groups: required comma-separated by fields, optional pagination/sort/filter; implements domain/schema types, FastAPI handler with validation, service/repository grouping SQL, CLI command, auth mapping, and tests.

Changes

Span Groups Listing

Layer / File(s) Summary
API contract and type definitions
openapi/ga/individual/platform.openapi.yaml, openapi/ga/openapi.yaml, openapi/openapi.yaml, services/intake/src/nmp/intake/spans/api/spans_schemas.py, services/intake/src/nmp/intake/spans/domain.py
OpenAPI and Pydantic schemas for SpanGroup, SpanGroupBy, SpanGroupSortField, and paginated SpanGroupsPage.
API endpoint handler and input validation
services/intake/src/nmp/intake/spans/api/spans.py
FastAPI list_span_groups endpoint; parses by CSV into SpanGroupBy, enforces non-empty/unique/allowed fields, applies parsed filter/time bounds, calls service, returns SpanGroupsPage.
Service orchestration
services/intake/src/nmp/intake/spans/service.py
IntakeSpansService.list_span_groups forwards filters, group_by, pagination, and sort to repository and returns paginated domain SpanGroup results.
Repository: grouped query implementation
services/intake/src/nmp/intake/spans/span_repository.py
Implements list_span_groups building ClickHouse aggregate SQL, SPAN_GROUP_COLUMN_FIELDS mapping, _GroupExpression dataclass, _group_expressions, _group_order_by, _row_to_group, pagination and ordering.
CLI integration
packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/intake/spans/__init__.py, packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/intake/spans/groups.py
Registers groups subcommand and implements groups list with by, filters, pagination, sort, output formatting, and all-pages support.
Authorization and docs
services/core/auth/src/nmp/core/auth/assets/static-authz.yaml, services/intake/README.md
Auth mapping for the new endpoint (intake.spans.list with intake:read, platform:read) and README entry.
Test coverage
services/intake/tests/test_spans_clickhouse_repository.py, services/intake/tests/test_spans_schemas.py, services/intake/tests/integration/spans/test_spans_read_filters.py
Unit and integration tests for schema mapping, repository SQL/filter behavior, invalid group-by rejection, and end-to-end grouping by session_id/trace_id with filters.

Sequence Diagram

sequenceDiagram
  participant Client
  participant IntakeAPI
  participant SpansService
  participant SpanRepository
  participant ClickHouse
  Client->>IntakeAPI: GET /spans/groups?workspace={w}&by=session_id,trace_id&...
  IntakeAPI->>SpansService: list_span_groups(filters, group_by, page, page_size, sort)
  SpansService->>SpanRepository: list_span_groups(filters, group_by, page, page_size, sort)
  SpanRepository->>ClickHouse: execute aggregated GROUP BY SQL (COUNT per group)
  ClickHouse-->>SpanRepository: rows (group keys + span_count)
  SpanRepository-->>SpansService: PaginatedResult[SpanGroup]
  SpansService-->>IntakeAPI: paginated domain groups
  IntakeAPI-->>Client: 200 SpanGroupsPage (data, grouped_by, pagination)
Loading

Suggested Reviewers

  • asutermo
  • svvarom
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.78% 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 Title accurately summarizes the main change: a new endpoint to group spans by trace_id and session_id, which is the core addition across all modified files.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch brnewsom/fp-203-add-group_by-support-for-span-queries-to-sessions

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

@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: 5

🧹 Nitpick comments (1)
openapi/openapi.yaml (1)

4105-4112: ⚡ Quick win

Type by as CSV array of SpanGroupBy instead of free-form string.

Line 4108 uses type: string, so OpenAPI can’t enforce allowed values or uniqueness. Model it as array + explode: false + items: SpanGroupBy for accurate SDK generation and stronger contract validation.

Suggested schema improvement
       - name: by
         in: query
         required: true
+        explode: false
         schema:
-          type: string
-          description: Comma-separated span fields to group by, e.g. trace_id or session_id,trace_id.
+          type: array
+          minItems: 1
+          uniqueItems: true
+          items:
+            $ref: '`#/components/schemas/SpanGroupBy`'
+          description: Comma-separated span fields to group by.
           title: By
-        description: Comma-separated span fields to group by, e.g. trace_id or session_id,trace_id.
+        description: Comma-separated span fields to group by.
🤖 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 `@openapi/openapi.yaml` around lines 4105 - 4112, Change the query parameter
"by" from a free-form string to a CSV-style array of the enum type SpanGroupBy
so OpenAPI can validate values and uniqueness: replace schema: type: string with
schema: type: array and items: { $ref: '`#/components/schemas/SpanGroupBy`' } and
add style: form and explode: false on the parameter (keep required: true and
descriptions) so SDKs generate a CSV array of SpanGroupBy values instead of an
unconstrained string.
🤖 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/ga/individual/platform.openapi.yaml`:
- Around line 4151-4162: Add a 400 response entry to this operation to cover
invalid/duplicate/empty "by" group-by parameter errors: insert a '400' response
alongside the existing '200' and '422' responses and reference an appropriate
schema (e.g., reuse components.schemas.HTTPValidationError or add a new
components schema like InvalidByParamError) so generated clients and API
contracts reflect the route’s actual behavior; update the operation that
currently references SpanGroupsPage to include this '400' response with
application/json content and the chosen schema.
- Around line 16313-16315: The schema for SpanGroup.group currently forces all
map values to type string ("additionalProperties: type: string"), but group keys
like trace_id can be null; update the additionalProperties schema for
SpanGroup.group to allow nulls (e.g., use type: [ "string", "null" ] or an
equivalent oneOf with string and null) so grouped results with null keys pass
validation; target the SpanGroup.group definition and change its
additionalProperties accordingly.

In `@openapi/ga/openapi.yaml`:
- Around line 4157-4162: The OpenAPI spec currently only documents a '422'
response for validation errors but does not document the '400' bad request
returned for invalid/duplicate/unsupported "by" query values; add a '400'
response object alongside '422' under the same operation and point it to the
same or a suitable schema (e.g., '`#/components/schemas/HTTPValidationError`' or a
new '`#/components/schemas/HTTPBadRequestError`'), include a clear description
like "Bad Request — invalid or unsupported 'by' parameter", and ensure the
operation's responses section lists both '400' and '422' so clients can
distinguish bad requests from validation errors.
- Around line 4105-4112: The query parameter named "by" is declared as a
free-form string but should enforce the allowed values from the SpanGroupBy
enum; update the "by" parameter to be a query array using items: $ref to the
components schema SpanGroupBy (i.e., schema: { type: array, items: { $ref:
'`#/components/schemas/SpanGroupBy`' } }) and set style: form and explode: false
so clients send comma-separated values, and update the description to reflect
it's a comma-separated list of SpanGroupBy enum values.

In `@openapi/openapi.yaml`:
- Around line 4151-4162: Add a 400 response to the OpenAPI responses for the
operation that returns SpanGroupsPage to represent invalid/duplicate "by"
parameter values: update the responses block alongside the existing '200' and
'422' entries to include a '400' entry with a description like "Invalid 'by'
parameter" and point its content schema to the existing HTTPValidationError (or
a new HTTPError schema if preferred) so clients can reliably handle semantic
validation errors for the "by" query parameter.

---

Nitpick comments:
In `@openapi/openapi.yaml`:
- Around line 4105-4112: Change the query parameter "by" from a free-form string
to a CSV-style array of the enum type SpanGroupBy so OpenAPI can validate values
and uniqueness: replace schema: type: string with schema: type: array and items:
{ $ref: '`#/components/schemas/SpanGroupBy`' } and add style: form and explode:
false on the parameter (keep required: true and descriptions) so SDKs generate a
CSV array of SpanGroupBy values instead of an unconstrained string.
🪄 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: 2bab772e-f3b9-4705-b8d4-c26db13dec78

📥 Commits

Reviewing files that changed from the base of the PR and between 39b6c19 and f9dbffe.

⛔ Files ignored due to path filters (16)
  • 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/cli/commands/api/intake/spans/__init__.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/commands/api/intake/spans/groups.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/resources/intake/api.md is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/resources/intake/spans/__init__.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/resources/intake/spans/groups.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/resources/intake/spans/spans.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/types/intake/spans/__init__.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/types/intake/spans/group_list_params.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/types/intake/spans/span_group.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/types/intake/spans/span_group_by.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/types/intake/spans/span_group_sort_field.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/types/intake/spans/span_groups_page.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/api_resources/intake/spans/test_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
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/intake/spans/__init__.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/intake/spans/groups.py
  • services/core/auth/src/nmp/core/auth/assets/static-authz.yaml
  • services/intake/README.md
  • services/intake/src/nmp/intake/spans/api/spans.py
  • services/intake/src/nmp/intake/spans/api/spans_schemas.py
  • services/intake/src/nmp/intake/spans/domain.py
  • services/intake/src/nmp/intake/spans/service.py
  • services/intake/src/nmp/intake/spans/span_repository.py
  • services/intake/tests/integration/spans/test_spans_read_filters.py
  • services/intake/tests/test_spans_clickhouse_repository.py
  • services/intake/tests/test_spans_schemas.py

Comment thread openapi/ga/individual/platform.openapi.yaml
Comment thread openapi/ga/individual/platform.openapi.yaml
Comment thread openapi/ga/openapi.yaml
Comment thread openapi/ga/openapi.yaml
Comment thread openapi/openapi.yaml
@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 19123/25199 75.9% 62.4%
Integration Tests 12197/23971 50.9% 26.3%

Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
@BrianNewsom
BrianNewsom added this pull request to the merge queue Jun 11, 2026
Merged via the queue into main with commit c4280a6 Jun 11, 2026
44 checks passed
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