Skip to main content
Dashboard filters let you narrow the sessions table by computed values - turn count, model used, error presence, or anything else you can derive from a session. Register filters using app.dashboard.filter() in your evals file.

How filters work

Each filter function runs against a session and returns a value. The return type determines the UI control automatically:
Return typeUI controlBehavior
booleanThree-state toggleCycle: All → Yes → No → All
numberRange sliderDual-handle slider with min/max inputs
stringMulti-select dropdownCheckboxes with Select All / Clear
Filtering and pagination happen server-side. Only the matching page is sent to the client. Filter values are computed incrementally - only new or changed sessions are reprocessed on each load.

Examples

// Boolean: sessions with subagents
app.dashboard.filter('uses-subagents',
  ({ stats }) => stats.subagentCount > 0,
  { label: 'Uses Subagents' }
);

// Number: filter by turn count
app.dashboard.filter('turn-count',
  ({ stats }) => stats.turnCount,
  { label: 'Turn Count' }
);

// String: filter by primary model
app.dashboard.filter('primary-model',
  ({ stats }) => stats.models[0] || 'unknown',
  { label: 'Primary Model' }
);

// String: duration bucket
app.dashboard.filter('duration-bucket', ({ stats }) => {
  const ms = parseInt(stats.duration) || 0;
  if (ms < 60000) return 'Under 1m';
  if (ms < 300000) return '1-5m';
  if (ms < 900000) return '5-15m';
  return 'Over 15m';
}, { label: 'Duration' });

// With a per-filter condition
app.dashboard.filter('avg-tools-per-turn',
  ({ stats }) => stats.turnCount > 0
    ? Math.round(stats.toolCallCount / stats.turnCount * 10) / 10
    : 0,
  {
    label: 'Avg Tools/Turn',
    condition: ({ stats }) => stats.toolCallCount > 0,
  }
);

Options

OptionTypeDescription
labelstringHuman-readable tile label (defaults to the filter name)
conditionConditionFunctionPer-filter gate

Pre-built filters

Enable the built-in date range filter:
app.dashboard.filter({ preBuilt: ['lastModified'] });
NameUI controlDescription
'lastModified'Date range pickerFilters sessions by last modified date
Pre-built date filters operate directly on session metadata - they don’t parse session logs. Combine with custom filters:
app.dashboard.filter({ preBuilt: ['lastModified'] });
app.dashboard.filter('turn-count', ({ stats }) => stats.turnCount, { label: 'Turns' });

Filters using cached eval results

In a cachedOnly view, filter functions receive ctx.evalResults. This lets you build per-eval score sliders and composite boolean filters without re-running evals:
app.dashboard.view('eval-results', { cachedOnly: true, label: 'Eval Score Filters' })
  .filter('quality-score',
    (ctx) => ctx.evalResults?.['quality']?.score ?? 0,
    { label: 'Quality Score' }
  )
  .filter('all-passing', (ctx) => {
    if (!ctx.evalResults) return false;
    return Object.values(ctx.evalResults).every(r => r.pass);
  }, { label: 'All Evals Passing' });
Sessions appear in the view only after the background queue has processed them.

Global condition

Filters respect app.condition(). If the global condition returns false for a session, all filters are skipped for that session.
app.condition(({ entries }) => entries.length > 0);

Named views

For organizing filters into focused groups with their own routes, see Dashboard Views.