diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/aiInput.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/aiInput.tsx index 85f00a48234430..7892e36b60e885 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/aiInput.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/aiInput.tsx @@ -2,13 +2,20 @@ import {Fragment, useEffect, useEffectEvent, useLayoutEffect, useState} from 're import styled from '@emotion/styled'; import * as Sentry from '@sentry/react'; +import {Alert} from '@sentry/scraps/alert'; +import {Container} from '@sentry/scraps/layout'; +import {ExternalLink} from '@sentry/scraps/link'; + import {Button} from 'sentry/components/core/button'; -import {t} from 'sentry/locale'; +import {t, tct, tn} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {EventTransaction} from 'sentry/types/event'; import {defined} from 'sentry/utils'; import usePrevious from 'sentry/utils/usePrevious'; -import type {TraceItemResponseAttribute} from 'sentry/views/explore/hooks/useTraceItemDetails'; +import type { + TraceItemDetailsMeta, + TraceItemResponseAttribute, +} from 'sentry/views/explore/hooks/useTraceItemDetails'; import { getIsAiNode, getTraceNodeAttribute, @@ -170,13 +177,17 @@ function useInvalidRoleDetection(roles: string[]) { export function AIInputSection({ node, attributes, + attributesMeta, event, }: { node: TraceTreeNode; attributes?: TraceItemResponseAttribute[]; + attributesMeta?: TraceItemDetailsMeta; event?: EventTransaction; }) { const shouldRender = getIsAiNode(node) && hasAIInputAttribute(node, attributes, event); + const messagesMeta = attributesMeta?.['gen_ai.request.messages']?.meta as any; + const originalMessagesLength: number | undefined = messagesMeta?.['']?.len; let promptMessages = shouldRender ? getTraceNodeAttribute('gen_ai.request.messages', node, event, attributes) @@ -227,7 +238,12 @@ export function AIInputSection({ {messages} ) : null} - {Array.isArray(messages) ? : null} + {Array.isArray(messages) ? ( + + ) : null} {toolArgs ? ( ) : null} @@ -248,8 +264,16 @@ const MAX_MESSAGES_TO_SHOW = MAX_MESSAGES_AT_START + MAX_MESSAGES_AT_END; * As the whole message history takes up too much space we only show the first two (as those often contain the system and initial user prompt) * and the last messages with the option to expand */ -function MessagesArrayRenderer({messages}: {messages: AIMessage[]}) { +function MessagesArrayRenderer({ + messages, + originalLength, +}: { + messages: AIMessage[]; + originalLength?: number; +}) { const [isExpanded, setIsExpanded] = useState(messages.length <= MAX_MESSAGES_TO_SHOW); + const truncatedMessages = originalLength ? originalLength - messages.length : 0; + const isTruncated = truncatedMessages > 0; // Reset the expanded state when the messages length changes const previousMessagesLength = usePrevious(messages.length); @@ -259,6 +283,22 @@ function MessagesArrayRenderer({messages}: {messages: AIMessage[]}) { } }, [messages.length, previousMessagesLength]); + const truncationAlert = isTruncated ? ( + + + {tct( + 'Due to [link:size limitations], the oldest [count] got dropped from the history.', + { + count: tn('message', '%s messages', truncatedMessages), + link: ( + + ), + } + )} + + + ) : null; + const renderMessage = (message: AIMessage, index: number) => { return ( @@ -278,11 +318,17 @@ function MessagesArrayRenderer({messages}: {messages: AIMessage[]}) { }; if (isExpanded) { - return messages.map(renderMessage); + return ( + + {truncationAlert} + {messages.map(renderMessage)} + + ); } return ( + {truncationAlert} {messages.slice(0, MAX_MESSAGES_AT_START).map(renderMessage)}