diff --git a/ee/session_recordings/ai/error_clustering.py b/ee/session_recordings/ai/error_clustering.py
index edc06ff471355..7a3c12c44dec0 100644
--- a/ee/session_recordings/ai/error_clustering.py
+++ b/ee/session_recordings/ai/error_clustering.py
@@ -6,6 +6,7 @@
import pandas as pd
import numpy as np
from posthog.session_recordings.models.session_recording_event import SessionRecordingViewed
+from datetime import date
CLUSTER_REPLAY_ERRORS_TIMING = Histogram(
"posthog_session_recordings_cluster_replay_errors",
@@ -30,7 +31,7 @@ def error_clustering(team: Team, user: User):
if not results:
return []
- df = pd.DataFrame(results, columns=["session_id", "input", "embeddings"])
+ df = pd.DataFrame(results, columns=["session_id", "error", "embeddings", "timestamp"])
df["cluster"] = cluster_embeddings(df["embeddings"].tolist())
@@ -42,7 +43,7 @@ def error_clustering(team: Team, user: User):
def fetch_error_embeddings(team_id: int):
query = """
SELECT
- session_id, input, embeddings
+ session_id, input, embeddings, generation_timestamp
FROM
session_replay_embeddings
WHERE
@@ -76,13 +77,21 @@ def construct_response(df: pd.DataFrame, team: Team, user: User):
clusters = []
for cluster, rows in df.groupby("cluster"):
session_ids = rows["session_id"].unique()
- sample = rows.sample(n=1)[["session_id", "input"]].rename(columns={"input": "error"}).to_dict("records")[0]
+ sample = rows.sample(n=1)[["session_id", "error"]].to_dict("records")[0]
+
+ date_series = (
+ df.groupby([df["timestamp"].dt.date])
+ .size()
+ .reindex(pd.date_range(end=date.today(), periods=7), fill_value=0)
+ )
+ sparkline = dict(zip(date_series.index.astype(str), date_series))
clusters.append(
{
"cluster": cluster,
"sample": sample.get("error"),
"session_ids": np.random.choice(session_ids, size=DBSCAN_MIN_SAMPLES - 1),
"occurrences": rows.size,
+ "sparkline": sparkline,
"unique_sessions": len(session_ids),
"viewed": len(np.intersect1d(session_ids, viewed_session_ids, assume_unique=True)),
}
diff --git a/frontend/src/scenes/session-recordings/errors/SessionRecordingErrors.tsx b/frontend/src/scenes/session-recordings/errors/SessionRecordingErrors.tsx
index 4b2dd2d1abed3..8b73fbcc1f924 100644
--- a/frontend/src/scenes/session-recordings/errors/SessionRecordingErrors.tsx
+++ b/frontend/src/scenes/session-recordings/errors/SessionRecordingErrors.tsx
@@ -2,6 +2,7 @@ import { IconFeatures } from '@posthog/icons'
import { LemonButton, LemonTable, LemonTabs, Spinner } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { JSONViewer } from 'lib/components/JSONViewer'
+import { Sparkline } from 'lib/lemon-ui/Sparkline'
import { useState } from 'react'
import { urls } from 'scenes/urls'
@@ -45,6 +46,17 @@ export function SessionRecordingErrors(): JSX.Element {
},
width: '50%',
},
+ {
+ title: '',
+ render: (_, cluster) => {
+ return (
+
+ )
+ },
+ },
{
title: 'Occurrences',
dataIndex: 'occurrences',
diff --git a/frontend/src/types.ts b/frontend/src/types.ts
index a7f65e84c5473..be96494253ae8 100644
--- a/frontend/src/types.ts
+++ b/frontend/src/types.ts
@@ -906,6 +906,7 @@ export type ErrorCluster = {
sample: string
occurrences: number
session_ids: string[]
+ sparkline: Record
unique_sessions: number
viewed: number
}