Skip to content

perf(dashboard): aggregate session stats by source in SQL - #61120

Closed
mijanx wants to merge 1 commit into
NousResearch:mainfrom
mijanx:fix/session-stats-source-aggregate
Closed

perf(dashboard): aggregate session stats by source in SQL#61120
mijanx wants to merge 1 commit into
NousResearch:mainfrom
mijanx:fix/session-stats-source-aggregate

Conversation

@mijanx

@mijanx mijanx commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add SessionDB.session_count_by_source() so source badges can be counted with a grouped SQL aggregate
  • switch /api/sessions/stats to use that aggregate instead of materializing up to 10k list_sessions_rich() rows
  • keep the existing active_store semantics and API shape unchanged

Why

/api/sessions/stats only needs grouped source counts for small dashboard badges. Before this change it built rich session-list rows — including preview/last-active projection and compression-tip handling — just to count source labels.

That is unnecessary work on large stores. On one real store with 24,691 session rows, the old path took a median ~665ms and also only counted the first 10k materialized rows. The direct aggregate took ~29ms and counts the full listable store. On a 9,009-row profile, the old path was ~883ms vs ~17ms for the aggregate.

This PR deliberately does not change active_store; the existing field continues to mean the current non-archived store count. If the dashboard wants a separate open/unclosed-session metric later, that should be an explicit additive API/UI decision rather than hidden inside this perf fix.

Duplicate check

Searched open and closed PRs/issues in NousResearch/hermes-agent for:

  • active_store sessions stats
  • "Active in store"
  • "sessions/stats" "by_source"
  • "end_reason IS NULL" "sessions" "stats"
  • "unarchived" "sessions/stats"

No duplicate PRs or issues found.

Validation

  • python -m py_compile hermes_state.py hermes_cli/web_server.py tests/hermes_cli/test_dashboard_admin_endpoints.py
  • pytest tests/hermes_cli/test_dashboard_admin_endpoints.py::TestSessionManagementEndpoints::test_stats_not_shadowed_by_session_id_route tests/hermes_cli/test_dashboard_admin_endpoints.py::TestSessionManagementEndpoints::test_stats_source_counts_use_direct_aggregate -q -o 'addopts=' → 2 passed
  • pytest tests/hermes_cli/test_dashboard_admin_endpoints.py -q -o 'addopts=' → 74 passed
  • pytest tests/test_hermes_state.py -q -o 'addopts=' → 339 passed
  • ruff check hermes_state.py hermes_cli/web_server.py tests/hermes_cli/test_dashboard_admin_endpoints.py → clean
  • git diff --check → clean

@alt-glitch alt-glitch added type/perf Performance improvement or optimization comp/dashboard Web dashboard / control panel UI (dashboard/, landing) P3 Low — cosmetic, nice to have duplicate This issue or pull request already exists labels Jul 8, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Duplicate of #48921 — both add SessionDB.session_count_by_source() (a single GROUP BY over idx_sessions_source) and switch /api/sessions/stats off the O(N) list_sessions_rich() loop, and both fix #48914. #48921 is the earlier open PR (2026-06-19). This PR's COALESCE(s.source, 'cli') grouping is a minor variant of the same mechanism at the same code site. Consolidating onto the earliest open PR.

@mijanx

mijanx commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Closing as duplicate of #48921, which is the earlier open PR for the same /api/sessions/stats GROUP BY source optimization. The active_store semantics in this PR were intentionally left unchanged; any useful test/semantics notes can be carried over there if needed.

@mijanx mijanx closed this Jul 8, 2026
kshitijk4poor pushed a commit to kshitijk4poor/hermes-agent that referenced this pull request Jul 28, 2026
…0k rows

Replaces the O(N) list_sessions_rich histogram in /api/sessions/stats
with a single GROUP BY query, reducing response time from ~575ms to
<1ms on large databases.

Original PR NousResearch#48921 by @liuhao1024. Salvage fixes based on review
feedback from teknium1 and @wernerhp:

1. Preserve try/except guard — a DB error still degrades to empty
   by_source instead of failing the whole stats response.
2. GROUP BY COALESCE(source, 'cli') — the original GROUP BY source
   could emit duplicate 'cli' keys (NULL group + literal 'cli' group)
   that the dict comprehension silently dropped.
3. Add exclude_children=True — list_sessions_rich excludes subagent
   runs, delegates, and compression continuations by default; the
   bare GROUP BY counted all rows, inflating source counts.

Aggregate shape (exclude_children/include_archived/limit params)
adapted from closed duplicate NousResearch#61120 by @mijanx.

Closes NousResearch#48914
Co-authored-by: mijanx <mijanx@users.noreply.github.com>
@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Your aggregate shape (exclude_children/include_archived params, COALESCE in GROUP BY, test verifying list_sessions_rich is not called) was adapted into #73362. Credited as co-author in the commit. Thanks for the more thorough implementation!

kshitijk4poor pushed a commit that referenced this pull request Jul 28, 2026
…0k rows

Replaces the O(N) list_sessions_rich histogram in /api/sessions/stats
with a single GROUP BY query, reducing response time from ~575ms to
<1ms on large databases.

Original PR #48921 by @liuhao1024. Salvage fixes based on review
feedback from teknium1 and @wernerhp:

1. Preserve try/except guard — a DB error still degrades to empty
   by_source instead of failing the whole stats response.
2. GROUP BY COALESCE(source, 'cli') — the original GROUP BY source
   could emit duplicate 'cli' keys (NULL group + literal 'cli' group)
   that the dict comprehension silently dropped.
3. Add exclude_children=True — list_sessions_rich excludes subagent
   runs, delegates, and compression continuations by default; the
   bare GROUP BY counted all rows, inflating source counts.

Aggregate shape (exclude_children/include_archived/limit params)
adapted from closed duplicate #61120 by @mijanx.

Closes #48914
Co-authored-by: mijanx <mijanx@users.noreply.github.com>
randlee pushed a commit to randlee/hermes-agent that referenced this pull request Aug 11, 2026
…0k rows

Replaces the O(N) list_sessions_rich histogram in /api/sessions/stats
with a single GROUP BY query, reducing response time from ~575ms to
<1ms on large databases.

Original PR NousResearch#48921 by @liuhao1024. Salvage fixes based on review
feedback from teknium1 and @wernerhp:

1. Preserve try/except guard — a DB error still degrades to empty
   by_source instead of failing the whole stats response.
2. GROUP BY COALESCE(source, 'cli') — the original GROUP BY source
   could emit duplicate 'cli' keys (NULL group + literal 'cli' group)
   that the dict comprehension silently dropped.
3. Add exclude_children=True — list_sessions_rich excludes subagent
   runs, delegates, and compression continuations by default; the
   bare GROUP BY counted all rows, inflating source counts.

Aggregate shape (exclude_children/include_archived/limit params)
adapted from closed duplicate NousResearch#61120 by @mijanx.

Closes NousResearch#48914
Co-authored-by: mijanx <mijanx@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/dashboard Web dashboard / control panel UI (dashboard/, landing) duplicate This issue or pull request already exists P3 Low — cosmetic, nice to have type/perf Performance improvement or optimization

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants