diff --git a/app/src/pages/trace/SpanDetails.tsx b/app/src/pages/trace/SpanDetails.tsx index 8d27f54e06..05ad77e7be 100644 --- a/app/src/pages/trace/SpanDetails.tsx +++ b/app/src/pages/trace/SpanDetails.tsx @@ -86,7 +86,7 @@ import { } from "./__generated__/SpanDetailsQuery.graphql"; import { EditSpanAnnotationsButton } from "./EditSpanAnnotationsButton"; import { SpanCodeDropdown } from "./SpanCodeDropdown"; -import { SpanEvaluationsTable } from "./SpanEvaluationsTable"; +import { SpanFeedback } from "./SpanFeedback"; import { SpanToDatasetExampleDialog } from "./SpanToDatasetExampleDialog"; /** @@ -178,11 +178,6 @@ export function SpanDetails({ message timestamp } - spanEvaluations { - name - label - score - } documentRetrievalMetrics { evaluationName ndcg @@ -196,7 +191,10 @@ export function SpanDetails({ score explanation } - ...SpanEvaluationsTable_evals + spanAnnotations { + name + } + ...SpanFeedback_annotations } } } @@ -252,13 +250,13 @@ export function SpanDetails({ {span.spanEvaluations.length} + {span.spanAnnotations.length} } > {(selected) => { - return selected ? : null; + return selected ? : null; }} @@ -1637,10 +1635,6 @@ function SpanEventsList({ events }: { events: Span["events"] }) { ); } -function SpanEvaluations(props: { span: Span }) { - return ; -} - const attributesContextualHelp = ( diff --git a/app/src/pages/trace/SpanEvaluationsTable.tsx b/app/src/pages/trace/SpanFeedback.tsx similarity index 62% rename from app/src/pages/trace/SpanEvaluationsTable.tsx rename to app/src/pages/trace/SpanFeedback.tsx index 29ddce6220..bd6b4bf668 100644 --- a/app/src/pages/trace/SpanEvaluationsTable.tsx +++ b/app/src/pages/trace/SpanFeedback.tsx @@ -6,11 +6,16 @@ import { useReactTable, } from "@tanstack/react-table"; +import { Accordion, AccordionItem } from "@arizeai/components"; + import { PreformattedTextCell } from "@phoenix/components/table"; import { tableCSS } from "@phoenix/components/table/styles"; import { TableEmpty } from "@phoenix/components/table/TableEmpty"; -import { SpanEvaluationsTable_evals$key } from "./__generated__/SpanEvaluationsTable_evals.graphql"; +import { + SpanFeedback_annotations$data, + SpanFeedback_annotations$key, +} from "./__generated__/SpanFeedback_annotations.graphql"; const columns = [ { @@ -36,29 +41,15 @@ const columns = [ }, ]; -export function SpanEvaluationsTable(props: { - span: SpanEvaluationsTable_evals$key; +function SpanAnnotationsTable({ + annotations, +}: { + annotations: SpanFeedback_annotations$data["spanAnnotations"]; }) { - const data = useFragment( - graphql` - fragment SpanEvaluationsTable_evals on Span { - spanEvaluations { - name - label - score - explanation - } - } - `, - props.span - ); - const evaluations = useMemo(() => { - return [...data.spanEvaluations]; - }, [data.spanEvaluations]); - + const tableData = useMemo(() => [...annotations], [annotations]); const table = useReactTable({ columns, - data: evaluations, + data: tableData, getCoreRowModel: getCoreRowModel(), }); const rows = table.getRowModel().rows; @@ -107,3 +98,41 @@ export function SpanEvaluationsTable(props: { ); } + +export function SpanFeedback({ span }: { span: SpanFeedback_annotations$key }) { + const data = useFragment( + graphql` + fragment SpanFeedback_annotations on Span { + spanAnnotations { + name + label + score + explanation + annotatorKind + } + } + `, + span + ); + + const humanAnnotations = useMemo(() => { + return data.spanAnnotations.filter( + (annotation) => annotation.annotatorKind === "HUMAN" + ); + }, [data.spanAnnotations]); + const llmAnnotations = useMemo(() => { + return data.spanAnnotations.filter( + (annotation) => annotation.annotatorKind === "LLM" + ); + }, [data.spanAnnotations]); + return ( + + + + + + + + + ); +} diff --git a/app/src/pages/trace/__generated__/SpanDetailsQuery.graphql.ts b/app/src/pages/trace/__generated__/SpanDetailsQuery.graphql.ts index 8cdaf57d28..f9992e3df6 100644 --- a/app/src/pages/trace/__generated__/SpanDetailsQuery.graphql.ts +++ b/app/src/pages/trace/__generated__/SpanDetailsQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<353a86207783c9525898f58f87984ece>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -54,10 +54,8 @@ export type SpanDetailsQuery$data = { readonly value: string; } | null; readonly parentId: string | null; - readonly spanEvaluations: ReadonlyArray<{ - readonly label: string | null; + readonly spanAnnotations: ReadonlyArray<{ readonly name: string; - readonly score: number | null; }>; readonly spanKind: SpanKind; readonly startTime: string; @@ -66,7 +64,7 @@ export type SpanDetailsQuery$data = { readonly tokenCountCompletion: number | null; readonly tokenCountPrompt: number | null; readonly tokenCountTotal: number | null; - readonly " $fragmentSpreads": FragmentRefs<"SpanEvaluationsTable_evals">; + readonly " $fragmentSpreads": FragmentRefs<"SpanFeedback_annotations">; } | { // This will never be '%other', but we need some // value in case none of the concrete values match. @@ -272,20 +270,6 @@ v19 = { "storageKey": null }, v20 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "label", - "storageKey": null -}, -v21 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "score", - "storageKey": null -}, -v22 = { "alias": null, "args": null, "concreteType": "DocumentRetrievalMetrics", @@ -324,6 +308,20 @@ v22 = { ], "storageKey": null }, +v21 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "label", + "storageKey": null +}, +v22 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "score", + "storageKey": null +}, v23 = { "alias": null, "args": null, @@ -347,8 +345,8 @@ v24 = { "storageKey": null }, (v5/*: any*/), - (v20/*: any*/), (v21/*: any*/), + (v22/*: any*/), (v23/*: any*/) ], "storageKey": null @@ -393,26 +391,24 @@ return { "action": "THROW", "path": "span.events" }, + (v20/*: any*/), + (v24/*: any*/), { "alias": null, "args": null, - "concreteType": "SpanEvaluation", + "concreteType": "SpanAnnotation", "kind": "LinkedField", - "name": "spanEvaluations", + "name": "spanAnnotations", "plural": true, "selections": [ - (v5/*: any*/), - (v20/*: any*/), - (v21/*: any*/) + (v5/*: any*/) ], "storageKey": null }, - (v22/*: any*/), - (v24/*: any*/), { "args": null, "kind": "FragmentSpread", - "name": "SpanEvaluationsTable_evals" + "name": "SpanFeedback_annotations" } ], "type": "Span", @@ -463,23 +459,30 @@ return { (v17/*: any*/), (v18/*: any*/), (v19/*: any*/), + (v20/*: any*/), + (v24/*: any*/), { "alias": null, "args": null, - "concreteType": "SpanEvaluation", + "concreteType": "SpanAnnotation", "kind": "LinkedField", - "name": "spanEvaluations", + "name": "spanAnnotations", "plural": true, "selections": [ (v5/*: any*/), - (v20/*: any*/), (v21/*: any*/), - (v23/*: any*/) + (v22/*: any*/), + (v23/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "annotatorKind", + "storageKey": null + } ], "storageKey": null - }, - (v22/*: any*/), - (v24/*: any*/) + } ], "type": "Span", "abstractKey": null @@ -490,16 +493,16 @@ return { ] }, "params": { - "cacheID": "994295103990e4b8fbff3b1422e1647f", + "cacheID": "1a2fd0a554755d7a72f92f71ca891a76", "id": null, "metadata": {}, "name": "SpanDetailsQuery", "operationKind": "query", - "text": "query SpanDetailsQuery(\n $spanId: GlobalID!\n) {\n span: node(id: $spanId) {\n __typename\n ... on Span {\n id\n context {\n spanId\n traceId\n }\n name\n spanKind\n statusCode: propagatedStatusCode\n statusMessage\n startTime\n parentId\n latencyMs\n tokenCountTotal\n tokenCountPrompt\n tokenCountCompletion\n input {\n value\n mimeType\n }\n output {\n value\n mimeType\n }\n attributes\n events {\n name\n message\n timestamp\n }\n spanEvaluations {\n name\n label\n score\n }\n documentRetrievalMetrics {\n evaluationName\n ndcg\n precision\n hit\n }\n documentEvaluations {\n documentPosition\n name\n label\n score\n explanation\n }\n ...SpanEvaluationsTable_evals\n }\n __isNode: __typename\n id\n }\n}\n\nfragment SpanEvaluationsTable_evals on Span {\n spanEvaluations {\n name\n label\n score\n explanation\n }\n}\n" + "text": "query SpanDetailsQuery(\n $spanId: GlobalID!\n) {\n span: node(id: $spanId) {\n __typename\n ... on Span {\n id\n context {\n spanId\n traceId\n }\n name\n spanKind\n statusCode: propagatedStatusCode\n statusMessage\n startTime\n parentId\n latencyMs\n tokenCountTotal\n tokenCountPrompt\n tokenCountCompletion\n input {\n value\n mimeType\n }\n output {\n value\n mimeType\n }\n attributes\n events {\n name\n message\n timestamp\n }\n documentRetrievalMetrics {\n evaluationName\n ndcg\n precision\n hit\n }\n documentEvaluations {\n documentPosition\n name\n label\n score\n explanation\n }\n spanAnnotations {\n name\n }\n ...SpanFeedback_annotations\n }\n __isNode: __typename\n id\n }\n}\n\nfragment SpanFeedback_annotations on Span {\n spanAnnotations {\n name\n label\n score\n explanation\n annotatorKind\n }\n}\n" } }; })(); -(node as any).hash = "39420c6270d451eca0528e7bb90fd065"; +(node as any).hash = "b2cb5f7c63f463b9b0e3b6f86724132f"; export default node; diff --git a/app/src/pages/trace/__generated__/SpanEvaluationsTable_evals.graphql.ts b/app/src/pages/trace/__generated__/SpanFeedback_annotations.graphql.ts similarity index 62% rename from app/src/pages/trace/__generated__/SpanEvaluationsTable_evals.graphql.ts rename to app/src/pages/trace/__generated__/SpanFeedback_annotations.graphql.ts index 629814be03..15d3f25d10 100644 --- a/app/src/pages/trace/__generated__/SpanEvaluationsTable_evals.graphql.ts +++ b/app/src/pages/trace/__generated__/SpanFeedback_annotations.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<<9c51d189ef5a77e30b1e811d5272a33e>> * @lightSyntaxTransform * @nogrep */ @@ -9,33 +9,35 @@ // @ts-nocheck import { Fragment, ReaderFragment } from 'relay-runtime'; +export type AnnotatorKind = "HUMAN" | "LLM"; import { FragmentRefs } from "relay-runtime"; -export type SpanEvaluationsTable_evals$data = { - readonly spanEvaluations: ReadonlyArray<{ +export type SpanFeedback_annotations$data = { + readonly spanAnnotations: ReadonlyArray<{ + readonly annotatorKind: AnnotatorKind; readonly explanation: string | null; readonly label: string | null; readonly name: string; readonly score: number | null; }>; - readonly " $fragmentType": "SpanEvaluationsTable_evals"; + readonly " $fragmentType": "SpanFeedback_annotations"; }; -export type SpanEvaluationsTable_evals$key = { - readonly " $data"?: SpanEvaluationsTable_evals$data; - readonly " $fragmentSpreads": FragmentRefs<"SpanEvaluationsTable_evals">; +export type SpanFeedback_annotations$key = { + readonly " $data"?: SpanFeedback_annotations$data; + readonly " $fragmentSpreads": FragmentRefs<"SpanFeedback_annotations">; }; const node: ReaderFragment = { "argumentDefinitions": [], "kind": "Fragment", "metadata": null, - "name": "SpanEvaluationsTable_evals", + "name": "SpanFeedback_annotations", "selections": [ { "alias": null, "args": null, - "concreteType": "SpanEvaluation", + "concreteType": "SpanAnnotation", "kind": "LinkedField", - "name": "spanEvaluations", + "name": "spanAnnotations", "plural": true, "selections": [ { @@ -65,6 +67,13 @@ const node: ReaderFragment = { "kind": "ScalarField", "name": "explanation", "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "annotatorKind", + "storageKey": null } ], "storageKey": null @@ -74,6 +83,6 @@ const node: ReaderFragment = { "abstractKey": null }; -(node as any).hash = "0604d02236eeb7e73817a68e2e6f12e2"; +(node as any).hash = "6199ecd4f0143cffbaa2fe5e19a8739c"; export default node; diff --git a/app/src/typeUtils.ts b/app/src/typeUtils.ts index d4fc0a3ef9..936a698c57 100644 --- a/app/src/typeUtils.ts +++ b/app/src/typeUtils.ts @@ -38,3 +38,10 @@ export function isStringArray(value: unknown): value is string[] { export function isObject(value: unknown): value is object { return typeof value === "object" && value !== null; } + +/** + * Makes a type mutable + */ +export type Mutable = { + -readonly [P in keyof T]: T[P]; +};