llmMonitoringCharts.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. import styled from '@emotion/styled';
  2. import {t} from 'sentry/locale';
  3. import {space} from 'sentry/styles/space';
  4. import type {MetricsQueryApiResponseLastMeta} from 'sentry/types/metrics';
  5. import {MetricDisplayType} from 'sentry/utils/metrics/types';
  6. import {useMetricsQuery} from 'sentry/utils/metrics/useMetricsQuery';
  7. import usePageFilters from 'sentry/utils/usePageFilters';
  8. import {MetricChartContainer} from 'sentry/views/dashboards/metrics/chart';
  9. interface TotalTokensUsedChartProps {
  10. groupId?: string;
  11. }
  12. export function TotalTokensUsedChart({groupId}: TotalTokensUsedChartProps) {
  13. const {selection, isReady: isGlobalSelectionReady} = usePageFilters();
  14. let query = 'span.category:"ai"';
  15. if (groupId) {
  16. query = `${query} span.ai.pipeline.group:"${groupId}"`;
  17. }
  18. const {
  19. data: timeseriesData,
  20. isLoading,
  21. isError,
  22. error,
  23. } = useMetricsQuery(
  24. [
  25. {
  26. name: 'total',
  27. mri: `c:spans/ai.total_tokens.used@none`,
  28. op: 'sum',
  29. query,
  30. },
  31. ],
  32. selection,
  33. {
  34. intervalLadder: 'dashboard',
  35. }
  36. );
  37. if (!isGlobalSelectionReady) {
  38. return null;
  39. }
  40. if (isError) {
  41. return <div>{'' + error}</div>;
  42. }
  43. return (
  44. <TokenChartContainer>
  45. <PanelTitle>{t('Total tokens used')}</PanelTitle>
  46. <MetricChartContainer
  47. timeseriesData={timeseriesData}
  48. isLoading={isLoading}
  49. metricQueries={[
  50. {
  51. name: 'mql',
  52. formula: '$total',
  53. },
  54. ]}
  55. displayType={MetricDisplayType.LINE}
  56. chartHeight={200}
  57. />
  58. </TokenChartContainer>
  59. );
  60. }
  61. interface NumberOfPipelinesChartProps {
  62. groupId?: string;
  63. }
  64. export function NumberOfPipelinesChart({groupId}: NumberOfPipelinesChartProps) {
  65. const {selection, isReady: isGlobalSelectionReady} = usePageFilters();
  66. let query = 'span.category:"ai.pipeline"';
  67. if (groupId) {
  68. query = `${query} span.group:"${groupId}"`;
  69. }
  70. const {
  71. data: timeseriesData,
  72. isLoading,
  73. isError,
  74. error,
  75. } = useMetricsQuery(
  76. [
  77. {
  78. name: 'number',
  79. mri: `d:spans/exclusive_time_light@millisecond`,
  80. op: 'count',
  81. query,
  82. },
  83. ],
  84. selection,
  85. {
  86. intervalLadder: 'dashboard',
  87. }
  88. );
  89. if (!isGlobalSelectionReady) {
  90. return null;
  91. }
  92. if (isError) {
  93. return <div>{'' + error}</div>;
  94. }
  95. return (
  96. <TokenChartContainer>
  97. <PanelTitle>{t('Number of AI pipelines')}</PanelTitle>
  98. <MetricChartContainer
  99. timeseriesData={timeseriesData}
  100. isLoading={isLoading}
  101. metricQueries={[
  102. {
  103. name: 'mql',
  104. formula: '$number',
  105. },
  106. ]}
  107. displayType={MetricDisplayType.LINE}
  108. chartHeight={200}
  109. />
  110. </TokenChartContainer>
  111. );
  112. }
  113. interface PipelineDurationChartProps {
  114. groupId?: string;
  115. }
  116. export function PipelineDurationChart({groupId}: PipelineDurationChartProps) {
  117. const {selection, isReady: isGlobalSelectionReady} = usePageFilters();
  118. let query = 'span.category:"ai.pipeline"';
  119. if (groupId) {
  120. query = `${query} span.group:"${groupId}"`;
  121. }
  122. const {
  123. data: timeseriesData,
  124. isLoading,
  125. isError,
  126. error,
  127. } = useMetricsQuery(
  128. [
  129. {
  130. name: 'duration',
  131. mri: `d:spans/duration@millisecond`,
  132. op: 'avg',
  133. query,
  134. },
  135. ],
  136. selection,
  137. {
  138. intervalLadder: 'dashboard',
  139. }
  140. );
  141. const lastMeta = timeseriesData?.meta?.findLast(_ => true);
  142. if (lastMeta && lastMeta.length >= 2) {
  143. // TODO hack: there is a bug somewhere that is dropping the unit
  144. (lastMeta[1] as MetricsQueryApiResponseLastMeta).unit ??= 'millisecond';
  145. }
  146. if (!isGlobalSelectionReady) {
  147. return null;
  148. }
  149. if (isError) {
  150. return <div>{'' + error}</div>;
  151. }
  152. return (
  153. <TokenChartContainer>
  154. <PanelTitle>{t('AI pipeline duration')}</PanelTitle>
  155. <MetricChartContainer
  156. timeseriesData={timeseriesData}
  157. isLoading={isLoading}
  158. metricQueries={[
  159. {
  160. name: 'mql',
  161. formula: '$duration',
  162. },
  163. ]}
  164. displayType={MetricDisplayType.LINE}
  165. chartHeight={200}
  166. />
  167. </TokenChartContainer>
  168. );
  169. }
  170. const PanelTitle = styled('h5')`
  171. padding: ${space(3)} ${space(3)} 0;
  172. margin: 0;
  173. `;
  174. const TokenChartContainer = styled('div')`
  175. border: 1px solid ${p => p.theme.border};
  176. border-radius: ${p => p.theme.borderRadius};
  177. height: 100%;
  178. display: flex;
  179. flex-direction: column;
  180. `;