-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(llm-o): connect Session replay to LLM Observability #29224
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Summary
Enhanced UI for LLM events in session recordings with bidirectional linking between session replays and LLM traces, providing better context for AI-related activities.
- Added
AIEventItems.tsx
withAIEventExpanded
andAIAIEventSummary
components to display AI event details and error states - Modified
ItemEvent.tsx
to support AI events ($ai_generation, $ai_span, $ai_trace) with a dedicated "Conversation" tab and "View LLM Trace" button - Added
hasSessionID
andgetSessionID
utility functions inutils.ts
to enable navigation between LLM traces and session recordings - Updated cost definitions for new AI models including 'claude-3.7-sonnet:thinking', 'deepseek-chat', and 'gemini-2.0-flash-lite-001'
7 file(s) reviewed, 5 comment(s)
Edit PR Review Bot Settings | Greptile
frontend/src/scenes/session-recordings/player/inspector/components/AIEventItems.tsx
Outdated
Show resolved
Hide resolved
let input = event.properties.$ai_input_state | ||
let output = event.properties.$ai_output_state ?? event.properties.$ai_error | ||
let raisedError = event.properties.$ai_is_error | ||
if (event.event === '$ai_generation') { | ||
input = event.properties.$ai_input | ||
output = event.properties.$ai_output_choices ?? event.properties.$ai_output | ||
raisedError = event.properties.$ai_is_error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Redundant reassignment of raisedError on line 15 since it's already set on line 11 with the same value
let input = event.properties.$ai_input_state | |
let output = event.properties.$ai_output_state ?? event.properties.$ai_error | |
let raisedError = event.properties.$ai_is_error | |
if (event.event === '$ai_generation') { | |
input = event.properties.$ai_input | |
output = event.properties.$ai_output_choices ?? event.properties.$ai_output | |
raisedError = event.properties.$ai_is_error | |
let input = event.properties.$ai_input_state | |
let output = event.properties.$ai_output_state ?? event.properties.$ai_error | |
let raisedError = event.properties.$ai_is_error | |
if (event.event === '$ai_generation') { | |
input = event.properties.$ai_input | |
output = event.properties.$ai_output_choices ?? event.properties.$ai_output |
@@ -92,9 +97,20 @@ export function ItemEvent({ item }: ItemEventProps): JSX.Element { | |||
} | |||
|
|||
export function ItemEventDetail({ item }: ItemEventProps): JSX.Element { | |||
// // Check if this is an LLM-related event |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
syntax: There's an extra comment slash here that should be removed
// // Check if this is an LLM-related event | |
// Check if this is an LLM-related event |
<Link to={urls.replay(undefined, undefined, getSessionID(event) ?? '')}> | ||
<IconOpenInNew /> | ||
View session recording | ||
</Link> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: The icon and text should be wrapped in a flex container with proper alignment and spacing for better visual consistency with other links in the UI.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be a and would get pretty much the same styling but with gap between icon and text
} | ||
return '$session_id' in event |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Redundant check for 'properties' in event since isLLMTraceEvent already confirms this property exists
} | |
return '$session_id' in event | |
return typeof event.properties.$session_id === 'string' | |
return '$session_id' in event |
Size Change: +2.48 kB (+0.03%) Total Size: 9.72 MB
|
…ents/AIEventItems.tsx Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
@@ -145,6 +147,14 @@ export function EventDetails({ event, tableProps }: EventDetailsProps): JSX.Elem | |||
label: 'Conversation', | |||
content: ( | |||
<div className="mx-3 -mt-2 mb-2 space-y-2"> | |||
{event.properties.$session_id ? ( | |||
<div className="flex flex-row items-center gap-2"> | |||
<Link to={urls.replay(undefined, undefined, event.properties.$session_id)}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
commented elsewhere this could be a lemon button
@@ -145,6 +147,14 @@ export function EventDetails({ event, tableProps }: EventDetailsProps): JSX.Elem | |||
label: 'Conversation', | |||
content: ( | |||
<div className="mx-3 -mt-2 mb-2 space-y-2"> | |||
{event.properties.$session_id ? ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you know it's a web event you can gate on recording being active
in error tracking
issueProperties['$session_id'] && issueProperties['$recording_status'] === 'active'
but that's not essential - we just can't guarantee that the presence of a session id means there will be a recording.
const isAIEvent = | ||
item.data.event === '$ai_generation' || item.data.event === '$ai_span' || item.data.event === '$ai_trace' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit-picking we could put this in taxonomy.ts but am only imagineering
) : item.data.event === '$ai_generation' || | ||
item.data.event === '$ai_span' || | ||
item.data.event === '$ai_trace' ? ( | ||
<AIEventSummary event={item.data} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, i think you need eager loading of the properties for the error flag in here to work...
here we subscribe to web vitals events and preload their event properties
posthog/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.ts
Line 1165 in 3f0512f
webVitalsEvents: (value: RecordingEventType[]) => { |
we delay loading properties in the inspector list until expanded otherwise
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a note on preloading but that could even be a follow-up
this is great!
{event.properties.$session_id ? ( | ||
<div className="flex flex-row items-center gap-2"> | ||
<Link to={urls.replay(undefined, undefined, event.properties.$session_id)}> | ||
<IconOpenInNew /> | ||
View session recording | ||
</Link> | ||
</div> | ||
) : null} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What Paul is suggesting would basically look like this:
{event.properties.$session_id ? ( | |
<div className="flex flex-row items-center gap-2"> | |
<Link to={urls.replay(undefined, undefined, event.properties.$session_id)}> | |
<IconOpenInNew /> | |
View session recording | |
</Link> | |
</div> | |
) : null} | |
{mightHaveRecording(event.properties) && ( | |
<ViewRecordingButton sessionId={event.properties.$session_id} /> | |
)} |
{hasSessionID(event) && ( | ||
<div className="flex flex-row items-center gap-2"> | ||
<Link to={urls.replay(undefined, undefined, getSessionID(event) ?? '')}> | ||
<IconOpenInNew /> | ||
View session recording | ||
</Link> | ||
</div> | ||
)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as my comment above
Problem
Not easy to connect LLM events and replys
Changes