PipelinesTable.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. import {browserHistory} from 'react-router';
  2. import type {Location} from 'history';
  3. import type {GridColumnHeader} from 'sentry/components/gridEditable';
  4. import GridEditable, {COL_WIDTH_UNDEFINED} from 'sentry/components/gridEditable';
  5. import Link from 'sentry/components/links/link';
  6. import type {CursorHandler} from 'sentry/components/pagination';
  7. import Pagination from 'sentry/components/pagination';
  8. import {t} from 'sentry/locale';
  9. import type {Organization} from 'sentry/types/organization';
  10. import type {EventsMetaType} from 'sentry/utils/discover/eventView';
  11. import {getFieldRenderer} from 'sentry/utils/discover/fieldRenderers';
  12. import type {Sort} from 'sentry/utils/discover/fields';
  13. import {RATE_UNIT_TITLE, RateUnit} from 'sentry/utils/discover/fields';
  14. import {VisuallyCompleteWithData} from 'sentry/utils/performanceForSentry';
  15. import {decodeScalar, decodeSorts} from 'sentry/utils/queryString';
  16. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  17. import {useLocation} from 'sentry/utils/useLocation';
  18. import useOrganization from 'sentry/utils/useOrganization';
  19. import {normalizeUrl} from 'sentry/utils/withDomainRequired';
  20. import {renderHeadCell} from 'sentry/views/starfish/components/tableCells/renderHeadCell';
  21. import {useSpanMetrics} from 'sentry/views/starfish/queries/useSpanMetrics';
  22. import type {MetricsResponse} from 'sentry/views/starfish/types';
  23. import {QueryParameterNames} from 'sentry/views/starfish/views/queryParameters';
  24. import {DataTitles} from 'sentry/views/starfish/views/spans/types';
  25. type Row = Pick<
  26. MetricsResponse,
  27. | 'project.id'
  28. | 'span.description'
  29. | 'span.group'
  30. | 'spm()'
  31. | 'avg(span.self_time)'
  32. | 'sum(span.self_time)'
  33. | 'time_spent_percentage()'
  34. >;
  35. type Column = GridColumnHeader<
  36. 'span.description' | 'spm()' | 'avg(span.self_time)' | 'time_spent_percentage()'
  37. >;
  38. const COLUMN_ORDER: Column[] = [
  39. {
  40. key: 'span.description',
  41. name: t('AI Pipeline name'),
  42. width: COL_WIDTH_UNDEFINED,
  43. },
  44. {
  45. key: 'spm()',
  46. name: `${t('Times')} ${RATE_UNIT_TITLE[RateUnit.PER_MINUTE]}`,
  47. width: COL_WIDTH_UNDEFINED,
  48. },
  49. {
  50. key: `avg(span.self_time)`,
  51. name: DataTitles.avg,
  52. width: COL_WIDTH_UNDEFINED,
  53. },
  54. {
  55. key: 'time_spent_percentage()',
  56. name: DataTitles.timeSpent,
  57. width: COL_WIDTH_UNDEFINED,
  58. },
  59. ];
  60. const SORTABLE_FIELDS = ['avg(span.self_time)', 'spm()', 'time_spent_percentage()'];
  61. type ValidSort = Sort & {
  62. field: 'spm()' | 'avg(span.self_time)' | 'time_spent_percentage()';
  63. };
  64. export function isAValidSort(sort: Sort): sort is ValidSort {
  65. return (SORTABLE_FIELDS as unknown as string[]).includes(sort.field);
  66. }
  67. export function PipelinesTable() {
  68. const location = useLocation();
  69. const organization = useOrganization();
  70. const cursor = decodeScalar(location.query?.[QueryParameterNames.SPANS_CURSOR]);
  71. const sortField = decodeScalar(location.query?.[QueryParameterNames.SPANS_SORT]);
  72. let sort = decodeSorts(sortField).filter(isAValidSort)[0];
  73. if (!sort) {
  74. sort = {field: 'time_spent_percentage()', kind: 'desc'};
  75. }
  76. const {data, isLoading, meta, pageLinks, error} = useSpanMetrics({
  77. search: new MutableSearch('span.op:ai.pipeline.langchain'),
  78. fields: [
  79. 'project.id',
  80. 'span.group',
  81. 'span.description',
  82. 'spm()',
  83. 'avg(span.self_time)',
  84. 'sum(span.self_time)',
  85. 'time_spent_percentage()',
  86. ],
  87. sorts: [sort],
  88. limit: 25,
  89. cursor,
  90. });
  91. const handleCursor: CursorHandler = (newCursor, pathname, query) => {
  92. browserHistory.push({
  93. pathname,
  94. query: {...query, [QueryParameterNames.SPANS_CURSOR]: newCursor},
  95. });
  96. };
  97. return (
  98. <VisuallyCompleteWithData
  99. id="PipelinesTable"
  100. hasData={data.length > 0}
  101. isLoading={isLoading}
  102. >
  103. <GridEditable
  104. isLoading={isLoading}
  105. error={error}
  106. data={data}
  107. columnOrder={COLUMN_ORDER}
  108. columnSortBy={[
  109. {
  110. key: sort.field,
  111. order: sort.kind,
  112. },
  113. ]}
  114. grid={{
  115. renderHeadCell: column =>
  116. renderHeadCell({
  117. column,
  118. sort,
  119. location,
  120. sortParameterName: QueryParameterNames.SPANS_SORT,
  121. }),
  122. renderBodyCell: (column, row) =>
  123. renderBodyCell(column, row, meta, location, organization),
  124. }}
  125. location={location}
  126. />
  127. <Pagination pageLinks={pageLinks} onCursor={handleCursor} />
  128. </VisuallyCompleteWithData>
  129. );
  130. }
  131. function renderBodyCell(
  132. column: Column,
  133. row: Row,
  134. meta: EventsMetaType | undefined,
  135. location: Location,
  136. organization: Organization
  137. ) {
  138. if (column.key === 'span.description') {
  139. if (!row['span.description']) {
  140. return <span>(unknown)</span>;
  141. }
  142. if (!row['span.group']) {
  143. return <span>{row['span.description']}</span>;
  144. }
  145. return (
  146. <Link
  147. to={normalizeUrl(
  148. `/organizations/${organization.slug}/ai-analytics/pipelines/${row['span.group']}`
  149. )}
  150. >
  151. {row['span.description']}
  152. </Link>
  153. );
  154. }
  155. if (!meta || !meta?.fields) {
  156. return row[column.key];
  157. }
  158. const renderer = getFieldRenderer(column.key, meta.fields, false);
  159. const rendered = renderer(row, {
  160. location,
  161. organization,
  162. unit: meta.units?.[column.key],
  163. });
  164. return rendered;
  165. }