Skip to content

Commit

Permalink
feat(sessions): add trace latency p50 to session details
Browse files Browse the repository at this point in the history
  • Loading branch information
Parker-Stafford committed Oct 30, 2024
1 parent abe86bb commit afc600a
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 16 deletions.
2 changes: 2 additions & 0 deletions app/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -1182,6 +1182,7 @@ type ProjectSession implements Node {
sessionUser: String
startTime: DateTime!
endTime: DateTime!
projectId: GlobalID!

"""Duration of the session in seconds"""
durationS: Float!
Expand All @@ -1190,6 +1191,7 @@ type ProjectSession implements Node {
lastOutput: SpanIOValue
tokenUsage: TokenUsage!
traces(first: Int = 50, last: Int, after: String, before: String): TraceConnection!
traceLatencyMsQuantile(probability: Float!): Float
}

"""A connection to a list of items."""
Expand Down
13 changes: 13 additions & 0 deletions app/src/pages/trace/SessionDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { css } from "@emotion/react";

import { Flex, Text, View } from "@arizeai/components";

import { LatencyText } from "@phoenix/components/trace/LatencyText";
import { TokenCount } from "@phoenix/components/trace/TokenCount";

import {
Expand All @@ -15,9 +16,11 @@ import { SessionDetailsTraceList } from "./SessionDetailsTraceList";
function SessionDetailsHeader({
traceCount,
tokenUsage,
latencyP50,
}: {
traceCount: number;
tokenUsage?: NonNullable<SessionDetailsQuery$data["session"]>["tokenUsage"];
latencyP50?: number;
}) {
return (
<View
Expand Down Expand Up @@ -45,6 +48,14 @@ function SessionDetailsHeader({
/>
</Flex>
) : null}
{latencyP50 != null ? (
<Flex direction={"column"}>
<Text elementType={"h3"} textSize={"medium"} color={"text-700"}>
Latency P50
</Text>
<LatencyText latencyMs={latencyP50} textSize={"xlarge"} />
</Flex>
) : null}
</Flex>
</View>
);
Expand Down Expand Up @@ -72,6 +83,7 @@ export function SessionDetails(props: SessionDetailsProps) {
prompt
}
sessionId
latencyP50: traceLatencyMsQuantile(probability: 0.50)
traces {
edges {
trace: node {
Expand Down Expand Up @@ -135,6 +147,7 @@ export function SessionDetails(props: SessionDetailsProps) {
<SessionDetailsHeader
traceCount={data.session.numTraces ?? 0}
tokenUsage={data.session.tokenUsage}
latencyP50={data.session.latencyP50}
/>
<SessionDetailsTraceList traces={data.session.traces} />
</main>
Expand Down
22 changes: 18 additions & 4 deletions app/src/pages/trace/__generated__/SessionDetailsQuery.graphql.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 15 additions & 8 deletions src/phoenix/server/api/dataloaders/latency_ms_quantile.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,16 @@
ProjectRowId: TypeAlias = int
TimeInterval: TypeAlias = tuple[Optional[datetime], Optional[datetime]]
FilterCondition: TypeAlias = Optional[str]
DbFilter: TypeAlias = Optional[SQLColumnExpression[bool]]
Probability: TypeAlias = float
QuantileValue: TypeAlias = float

Segment: TypeAlias = tuple[Kind, TimeInterval, FilterCondition]
Segment: TypeAlias = tuple[Kind, TimeInterval, FilterCondition, DbFilter]
Param: TypeAlias = tuple[ProjectRowId, Probability]

Key: TypeAlias = tuple[Kind, ProjectRowId, Optional[TimeRange], FilterCondition, Probability]
Key: TypeAlias = tuple[
Kind, ProjectRowId, Optional[TimeRange], FilterCondition, Probability, DbFilter
]
Result: TypeAlias = Optional[QuantileValue]
ResultPosition: TypeAlias = int
DEFAULT_VALUE: Result = None
Expand All @@ -47,15 +50,15 @@


def _cache_key_fn(key: Key) -> tuple[Segment, Param]:
kind, project_rowid, time_range, filter_condition, probability = key
kind, project_rowid, time_range, filter_condition, probability, db_filter = key
interval = (
(time_range.start, time_range.end) if isinstance(time_range, TimeRange) else (None, None)
)
return (kind, interval, filter_condition), (project_rowid, probability)
return (kind, interval, filter_condition, db_filter), (project_rowid, probability)


_Section: TypeAlias = ProjectRowId
_SubKey: TypeAlias = tuple[TimeInterval, FilterCondition, Kind, Probability]
_SubKey: TypeAlias = tuple[TimeInterval, FilterCondition, DbFilter, Kind, Probability]


class LatencyMsQuantileCache(
Expand All @@ -71,8 +74,10 @@ def __init__(self) -> None:
)

def _cache_key(self, key: Key) -> tuple[_Section, _SubKey]:
(kind, interval, filter_condition), (project_rowid, probability) = _cache_key_fn(key)
return project_rowid, (interval, filter_condition, kind, probability)
(kind, interval, filter_condition, db_filter), (project_rowid, probability) = _cache_key_fn(
key
)
return project_rowid, (interval, filter_condition, db_filter, kind, probability)


class LatencyMsQuantileDataLoader(DataLoader[Key, Result]):
Expand Down Expand Up @@ -113,7 +118,7 @@ async def _get_results(
segment: Segment,
params: Mapping[Param, list[ResultPosition]],
) -> AsyncIterator[tuple[ResultPosition, QuantileValue]]:
kind, (start_time, end_time), filter_condition = segment
kind, (start_time, end_time), filter_condition, db_filter = segment
stmt = select(models.Trace.project_rowid)
if kind == "trace":
latency_column = cast(FloatCol, models.Trace.latency_ms)
Expand All @@ -131,6 +136,8 @@ async def _get_results(
stmt = stmt.where(start_time <= time_column)
if end_time:
stmt = stmt.where(time_column < end_time)
if db_filter:
stmt = stmt.where(db_filter)
if dialect is SupportedSQLDialect.POSTGRESQL:
results = _get_results_postgresql(session, stmt, latency_column, params)
elif dialect is SupportedSQLDialect.SQLITE:
Expand Down
4 changes: 2 additions & 2 deletions src/phoenix/server/api/types/Project.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ async def latency_ms_quantile(
time_range: Optional[TimeRange] = UNSET,
) -> Optional[float]:
return await info.context.data_loaders.latency_ms_quantile.load(
("trace", self.id_attr, time_range, None, probability),
("trace", self.id_attr, time_range, None, probability, None),
)

@strawberry.field
Expand All @@ -140,7 +140,7 @@ async def span_latency_ms_quantile(
filter_condition: Optional[str] = UNSET,
) -> Optional[float]:
return await info.context.data_loaders.latency_ms_quantile.load(
("span", self.id_attr, time_range, filter_condition, probability),
("span", self.id_attr, time_range, filter_condition, probability, None),
)

@strawberry.field
Expand Down
29 changes: 27 additions & 2 deletions src/phoenix/server/api/types/ProjectSession.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import strawberry
from openinference.semconv.trace import SpanAttributes
from sqlalchemy import select
from strawberry import UNSET, Info, lazy
from strawberry.relay import Connection, Node, NodeID
from strawberry import UNSET, Info, Private, lazy
from strawberry.relay import Connection, GlobalID, Node, NodeID

from phoenix.db import models
from phoenix.server.api.context import Context
Expand All @@ -22,11 +22,18 @@
class ProjectSession(Node):
_table: ClassVar[Type[models.ProjectSession]] = models.ProjectSession
id_attr: NodeID[int]
project_rowid: Private[int]
session_id: str
session_user: Optional[str]
start_time: datetime
end_time: datetime

@strawberry.field
async def project_id(self) -> GlobalID:
from phoenix.server.api.types.Project import Project

return GlobalID(type_name=Project.__name__, node_id=str(self.project_rowid))

@strawberry.field(description="Duration of the session in seconds") # type: ignore
async def duration_s(self) -> float:
return (self.end_time - self.start_time).total_seconds()
Expand Down Expand Up @@ -103,6 +110,23 @@ async def traces(
data = [to_gql_trace(trace) async for trace in traces]
return connection_from_list(data=data, args=args)

@strawberry.field
async def trace_latency_ms_quantile(
self,
info: Info[Context, None],
probability: float,
) -> Optional[float]:
return await info.context.data_loaders.latency_ms_quantile.load(
(
"trace",
self.project_rowid,
None,
None,
probability,
models.Trace.project_session_rowid == self.project_rowid,
)
)


def to_gql_project_session(project_session: models.ProjectSession) -> ProjectSession:
return ProjectSession(
Expand All @@ -111,6 +135,7 @@ def to_gql_project_session(project_session: models.ProjectSession) -> ProjectSes
session_user=project_session.session_user,
start_time=project_session.start_time,
end_time=project_session.end_time,
project_rowid=project_session.project_id,
)


Expand Down

0 comments on commit afc600a

Please sign in to comment.