feat(users-page): filter heartbeats + openclaw metadata in events path + Model column#4
Conversation
…nts; add Model column Three observability gaps on downstream OpenClaw deployments (2BB-294/295/298): 1) LiteLLM's internal health-check probes create trace rows tagged `litellm-internal-health-check`. Those were leaking into the Users page counts and into the Traces view. Exclude them from the events-table user aggregations and from `getTracesGroupedByUsers` / `getTotalUserCount` / `getUserMetrics`. 2) The Users page already reads Channel + Username from `trace.metadata['openclaw_channel']` + coalesced `openclaw_sender_username/name/label`, but the events-table aggregation path (`getUserMetricsFromEventsTable`) didn't. Add the same projection there so the columns stay populated under the events-backed code path. 3) LiteLLM names traces `litellm-<call_type>/<model>`. Surface the model as a first-class sortable column in the Traces table, preferring `metadata.model` when present and falling back to the suffix of the trace name.
|
@claude review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b0bbf38de1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| .where(appliedEventsFilter) | ||
| .whereRaw("e.user_id IS NOT NULL AND length(e.user_id) > 0") | ||
| .whereRaw("e.is_deleted = 0") | ||
| .whereRaw("NOT has(e.tags, 'litellm-internal-health-check')") |
There was a problem hiding this comment.
Align hasAny checks with heartbeat filtering
This exclusion is applied to user list/count/metrics queries, but hasAnyUser/hasAnyUserFromEventsTable still count heartbeat-tagged rows. In projects where heartbeat probes are the only tagged user rows, the users page will report data exists (so onboarding is hidden) while the table endpoints return zero rows, producing an inconsistent empty state. Apply the same heartbeat predicate in the hasAny* queries so the gating logic matches the filtered datasets.
Useful? React with 👍 / 👎.
Addresses Codex review on #4: getUsersFromEventsTable / getUsersCountFromEventsTable / getUserMetricsFromEventsTable / getTracesGroupedByUsers / getTotalUserCount / getUserMetrics all skip rows tagged `litellm-internal-health-check`, but hasAnyUser and hasAnyUserFromEventsTable did not. In projects where heartbeat probes are the only tagged user rows, the onboarding gate saw "data exists" while the filtered list/metrics endpoints returned empty — an inconsistent empty state where the wizard stayed hidden on a seemingly-empty page. Mirror the same `NOT has(tags, 'litellm-internal-health-check')` predicate on both hasAny* queries so the gating logic lines up with the filtered datasets.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e35a8d83c0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const metadata = row.original.metadata as | ||
| | { model?: unknown } | ||
| | null | ||
| | undefined; | ||
| const metadataModel = |
There was a problem hiding this comment.
Source model value from data that is actually loaded
This cell reads row.original.metadata.model, but the traces table rows do not include metadata in the data returned by api.traces.all (the shared TracesTableUiReturnType omits it, and the local row mapping only copies explicit fields), so metadataModel is always undefined. In practice, traces that carry metadata.model but whose name does not match the litellm-.../... pattern will render an empty Model column even though the model is available.
Useful? React with 👍 / 👎.
Addresses Codex P2 on #4: `TracesTableUiReturnType` does not expose `metadata`, and the row mapping only copies explicit fields, so `row.original.metadata.model` was always `undefined`. The live Model value comes from parsing the LiteLLM trace name (`litellm-<endpoint>/<model>`). Remove the dead branch and keep only the name-parsing fallback. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Summary
Closes three observability gaps visible on claw-world's OpenClaw deployments (Linear 2BB-294 / 2BB-295 / 2BB-298).
1. Hide LiteLLM heartbeat probes from Users + Traces (2BB-294)
LiteLLM's internal health-check probes create trace rows tagged
litellm-internal-health-check. Those were leaking into:Add `NOT has(..., 'litellm-internal-health-check')` to each path so internal probes never pollute user-facing aggregates.
2. Read openclaw_* metadata from the events path (2BB-295)
The Users page already reads Channel from `trace.metadata['openclaw_channel']` and Username from a coalesce of `openclaw_sender_username` / `_sender_name` / `_sender_label` (landed in #1). But the events-table aggregation path in `getUserMetricsFromEventsTable` didn't. Add the same projection using `mapFromArrays(arrayReverse(...))` so the columns stay populated under the events-backed code path.
3. Surface model as a first-class Traces column (2BB-298)
LiteLLM names traces `litellm-<call_type>/`. Add a Model column to the Traces table, preferring `metadata.model` when present and falling back to the suffix of the trace name. Hidden by default but enableable via the column picker.
Verified with
claw-world's `infra/test/` harness against this branch:
Users page shows alice/mattermost, bob/mattermost, carol/slack with Channel + Username columns populated; heartbeat-tagged rows absent from Traces view.
Related