diff --git a/app/src/components/trace/SpanItem.tsx b/app/src/components/trace/SpanItem.tsx index 9482f8c117..4802f55b11 100644 --- a/app/src/components/trace/SpanItem.tsx +++ b/app/src/components/trace/SpanItem.tsx @@ -3,8 +3,8 @@ import { css } from "@emotion/react"; import { Flex, Text, View } from "@arizeai/components"; +import { TokenCount } from "@phoenix/components/trace/TokenCount"; import { SpanStatusCode } from "@phoenix/pages/project/__generated__/SpansTable_spans.graphql"; -import { TokenCount } from "@phoenix/pages/project/TokenCount"; import { LatencyText } from "./LatencyText"; import { SpanKindLabel } from "./SpanKindLabel"; diff --git a/app/src/pages/project/TokenCount.tsx b/app/src/components/trace/TokenCount.tsx similarity index 77% rename from app/src/pages/project/TokenCount.tsx rename to app/src/components/trace/TokenCount.tsx index 36d7a3ae13..f74c30a346 100644 --- a/app/src/pages/project/TokenCount.tsx +++ b/app/src/components/trace/TokenCount.tsx @@ -6,6 +6,7 @@ import { Icon, Icons, Text, + TextProps, Tooltip, TooltipTrigger, TriggerWrap, @@ -24,6 +25,10 @@ type TokenCountProps = { * The number of tokens in the completion */ tokenCountCompletion: number; + /** + * The size of the icon and text + */ + textSize?: TextProps["textSize"]; }; /** @@ -33,7 +38,7 @@ export function TokenCount(props: TokenCountProps) { return ( - {props.tokenCountTotal} + {props.tokenCountTotal} @@ -51,16 +56,22 @@ export function TokenCount(props: TokenCountProps) { ); } -function TokenItem({ children }: { children: number }) { +function TokenItem({ + children, + ...textProps +}: { + children: number; + textSize?: TextProps["textSize"]; +}) { return ( - + } css={css` color: var(--ac-global-text-color-900); `} /> - {children} + {children} ); } diff --git a/app/src/components/trace/TraceTree.tsx b/app/src/components/trace/TraceTree.tsx index d1d7684617..9d4a60f87c 100644 --- a/app/src/components/trace/TraceTree.tsx +++ b/app/src/components/trace/TraceTree.tsx @@ -3,7 +3,7 @@ import { css } from "@emotion/react"; import { Flex, Text, View } from "@arizeai/components"; -import { TokenCount } from "@phoenix/pages/project/TokenCount"; +import { TokenCount } from "@phoenix/components/trace/TokenCount"; import { LatencyText } from "./LatencyText"; import { SpanKindIcon } from "./SpanKindIcon"; diff --git a/app/src/pages/project/SpansTable.tsx b/app/src/pages/project/SpansTable.tsx index 2328d6664f..4a9489e180 100644 --- a/app/src/pages/project/SpansTable.tsx +++ b/app/src/pages/project/SpansTable.tsx @@ -32,6 +32,7 @@ import { TimestampCell } from "@phoenix/components/table/TimestampCell"; import { LatencyText } from "@phoenix/components/trace/LatencyText"; import { SpanKindLabel } from "@phoenix/components/trace/SpanKindLabel"; import { SpanStatusCodeIcon } from "@phoenix/components/trace/SpanStatusCodeIcon"; +import { TokenCount } from "@phoenix/components/trace/TokenCount"; import { useStreamState } from "@phoenix/contexts/StreamStateContext"; import { useTracingContext } from "@phoenix/contexts/TracingContext"; @@ -52,7 +53,6 @@ import { EVALS_KEY_SEPARATOR, getGqlSort, } from "./tableUtils"; -import { TokenCount } from "./TokenCount"; type SpansTableProps = { project: SpansTable_spans$key; }; diff --git a/app/src/pages/project/TracesTable.tsx b/app/src/pages/project/TracesTable.tsx index 8960d41ffa..7befbadd8d 100644 --- a/app/src/pages/project/TracesTable.tsx +++ b/app/src/pages/project/TracesTable.tsx @@ -37,6 +37,7 @@ import { TimestampCell } from "@phoenix/components/table/TimestampCell"; import { LatencyText } from "@phoenix/components/trace/LatencyText"; import { SpanKindLabel } from "@phoenix/components/trace/SpanKindLabel"; import { SpanStatusCodeIcon } from "@phoenix/components/trace/SpanStatusCodeIcon"; +import { TokenCount } from "@phoenix/components/trace/TokenCount"; import { ISpanItem } from "@phoenix/components/trace/types"; import { createSpanTree, SpanTreeNode } from "@phoenix/components/trace/utils"; import { useStreamState } from "@phoenix/contexts/StreamStateContext"; @@ -59,7 +60,6 @@ import { EVALS_KEY_SEPARATOR, getGqlSort, } from "./tableUtils"; -import { TokenCount } from "./TokenCount"; type TracesTableProps = { project: TracesTable_spans$key; }; diff --git a/app/src/pages/trace/SpanAside.tsx b/app/src/pages/trace/SpanAside.tsx new file mode 100644 index 0000000000..258f0dfa2c --- /dev/null +++ b/app/src/pages/trace/SpanAside.tsx @@ -0,0 +1,145 @@ +import React, { PropsWithChildren, useMemo } from "react"; +import { graphql, useRefetchableFragment } from "react-relay"; +import { css } from "@emotion/react"; + +import { Flex, Text, View } from "@arizeai/components"; + +import { AnnotationLabel } from "@phoenix/components/annotation"; +import { LatencyText } from "@phoenix/components/trace/LatencyText"; +import { SpanStatusCodeIcon } from "@phoenix/components/trace/SpanStatusCodeIcon"; +import { TokenCount } from "@phoenix/components/trace/TokenCount"; +import { useSpanStatusCodeColor } from "@phoenix/components/trace/useSpanStatusCodeColor"; +import { fullTimeFormatter } from "@phoenix/utils/timeFormatUtils"; + +import { SpanAside_span$key } from "./__generated__/SpanAside_span.graphql"; +import { SpanAsideSpanQuery } from "./__generated__/SpanAsideSpanQuery.graphql"; + +const annotationListCSS = css` + display: flex; + padding-top: var(--ac-global-dimension-size-50); + flex-direction: column; + gap: var(--ac-global-dimension-size-100); + align-items: flex-start; +`; + +/** + * A component that shows the details of a span that is supplementary to the main span details + */ +export function SpanAside(props: { span: SpanAside_span$key }) { + const [data] = useRefetchableFragment( + graphql` + fragment SpanAside_span on Span + @refetchable(queryName: "SpanAsideSpanQuery") { + id + code: statusCode + startTime + endTime + tokenCountTotal + tokenCountPrompt + tokenCountCompletion + spanAnnotations { + id + name + label + annotatorKind + score + } + } + `, + props.span + ); + const { + startTime, + endTime, + code, + tokenCountCompletion, + tokenCountPrompt, + tokenCountTotal, + } = data; + const startDate = useMemo(() => new Date(startTime), [startTime]); + const endDate = useMemo( + () => (endTime ? new Date(endTime) : null), + [endTime] + ); + const latencyMs = useMemo(() => { + if (!endDate) return null; + return endDate.getTime() - startDate.getTime(); + }, [endDate, startDate]); + const statusColor = useSpanStatusCodeColor(code); + const annotations = data.spanAnnotations; + const hasAnnotations = annotations.length > 0; + return ( + + + + + + + {code} + + + + + {fullTimeFormatter(startDate)} + + {endDate && ( + + {fullTimeFormatter(endDate)} + + )} + + + {typeof latencyMs === "number" ? ( + + ) : ( + "--" + )} + + + {tokenCountTotal ? ( + + + + ) : null} + {hasAnnotations && ( + +
    + {annotations.map((annotation) => ( +
  • + +
  • + ))} +
+
+ )} +
+
+ ); +} + +function LabeledValue({ + label, + children, +}: PropsWithChildren<{ label: string }>) { + return ( + + + {label} + + {children} + + ); +} diff --git a/app/src/pages/trace/SpanDetails.tsx b/app/src/pages/trace/SpanDetails.tsx index 05ad77e7be..b2601f7e88 100644 --- a/app/src/pages/trace/SpanDetails.tsx +++ b/app/src/pages/trace/SpanDetails.tsx @@ -36,6 +36,8 @@ import { TabPane, Tabs, Text, + Tooltip, + TooltipTrigger, View, ViewProps, ViewStyleProps, @@ -59,9 +61,10 @@ import { MarkdownDisplayProvider, } from "@phoenix/components/markdown"; import { SpanKindIcon } from "@phoenix/components/trace"; -import { SpanItem } from "@phoenix/components/trace/SpanItem"; +import { SpanKindLabel } from "@phoenix/components/trace/SpanKindLabel"; import { useNotifySuccess, useTheme } from "@phoenix/contexts"; import { useFeatureFlag } from "@phoenix/contexts/FeatureFlagsContext"; +import { usePreferencesContext } from "@phoenix/contexts/PreferencesContext"; import { AttributeDocument, AttributeEmbedding, @@ -85,6 +88,7 @@ import { SpanDetailsQuery$data, } from "./__generated__/SpanDetailsQuery.graphql"; import { EditSpanAnnotationsButton } from "./EditSpanAnnotationsButton"; +import { SpanAside } from "./SpanAside"; import { SpanCodeDropdown } from "./SpanCodeDropdown"; import { SpanFeedback } from "./SpanFeedback"; import { SpanToDatasetExampleDialog } from "./SpanToDatasetExampleDialog"; @@ -195,6 +199,7 @@ export function SpanDetails({ name } ...SpanFeedback_annotations + ...SpanAside_span } } } @@ -214,6 +219,10 @@ export function SpanDetails({ return spanHasException(span); }, [span]); const showAnnotations = useFeatureFlag("annotations"); + const showSpanAside = usePreferencesContext((store) => store.showSpanAside); + const setShowSpanAside = usePreferencesContext( + (store) => store.setShowSpanAside + ); return ( - - + + + + {span.name} + - + +