@gooddata/sdk-ui-gen-ai 11.52.0 → 11.53.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/NOTICE +59 -9
  2. package/esm/components/ConfigContext.d.ts +1 -0
  3. package/esm/components/ConfigContext.d.ts.map +1 -1
  4. package/esm/components/GenAIChat.d.ts.map +1 -1
  5. package/esm/components/GenAIChat.js +2 -2
  6. package/esm/components/GenAiStore.d.ts +9 -0
  7. package/esm/components/GenAiStore.d.ts.map +1 -1
  8. package/esm/components/GenAiStore.js +2 -1
  9. package/esm/components/intelligence/GenAiInteractionIntelligence.d.ts +21 -0
  10. package/esm/components/intelligence/GenAiInteractionIntelligence.d.ts.map +1 -0
  11. package/esm/components/intelligence/GenAiInteractionIntelligence.js +57 -0
  12. package/esm/components/intelligence/InteractionIntelligenceTrigger.d.ts +15 -0
  13. package/esm/components/intelligence/InteractionIntelligenceTrigger.d.ts.map +1 -0
  14. package/esm/components/intelligence/InteractionIntelligenceTrigger.js +19 -0
  15. package/esm/components/intelligence/data/categoryDetailRows.d.ts +12 -0
  16. package/esm/components/intelligence/data/categoryDetailRows.d.ts.map +1 -0
  17. package/esm/components/intelligence/data/categoryDetailRows.js +206 -0
  18. package/esm/components/intelligence/data/constants.d.ts +22 -0
  19. package/esm/components/intelligence/data/constants.d.ts.map +1 -0
  20. package/esm/components/intelligence/data/constants.js +27 -0
  21. package/esm/components/intelligence/data/deriveInteractionIntelligenceFromSteps.d.ts +23 -0
  22. package/esm/components/intelligence/data/deriveInteractionIntelligenceFromSteps.d.ts.map +1 -0
  23. package/esm/components/intelligence/data/deriveInteractionIntelligenceFromSteps.js +109 -0
  24. package/esm/components/intelligence/data/mergeDetailRows.d.ts +15 -0
  25. package/esm/components/intelligence/data/mergeDetailRows.d.ts.map +1 -0
  26. package/esm/components/intelligence/data/mergeDetailRows.js +91 -0
  27. package/esm/components/intelligence/data/timeline.d.ts +18 -0
  28. package/esm/components/intelligence/data/timeline.d.ts.map +1 -0
  29. package/esm/components/intelligence/data/timeline.js +40 -0
  30. package/esm/components/intelligence/data/types.d.ts +159 -0
  31. package/esm/components/intelligence/data/types.d.ts.map +1 -0
  32. package/esm/components/intelligence/data/types.js +2 -0
  33. package/esm/components/intelligence/data/useInteractionIntelligenceData.d.ts +9 -0
  34. package/esm/components/intelligence/data/useInteractionIntelligenceData.d.ts.map +1 -0
  35. package/esm/components/intelligence/data/useInteractionIntelligenceData.js +20 -0
  36. package/esm/components/intelligence/data/useInteractionIntelligenceTotals.d.ts +9 -0
  37. package/esm/components/intelligence/data/useInteractionIntelligenceTotals.d.ts.map +1 -0
  38. package/esm/components/intelligence/data/useInteractionIntelligenceTotals.js +20 -0
  39. package/esm/components/intelligence/detail/DetailRowValue.d.ts +10 -0
  40. package/esm/components/intelligence/detail/DetailRowValue.d.ts.map +1 -0
  41. package/esm/components/intelligence/detail/DetailRowValue.js +23 -0
  42. package/esm/components/intelligence/detail/IntelligenceCategoryDetail.d.ts +17 -0
  43. package/esm/components/intelligence/detail/IntelligenceCategoryDetail.d.ts.map +1 -0
  44. package/esm/components/intelligence/detail/IntelligenceCategoryDetail.js +21 -0
  45. package/esm/components/intelligence/detail/IntelligenceDetailHeader.d.ts +12 -0
  46. package/esm/components/intelligence/detail/IntelligenceDetailHeader.d.ts.map +1 -0
  47. package/esm/components/intelligence/detail/IntelligenceDetailHeader.js +19 -0
  48. package/esm/components/intelligence/detail/IntelligenceDetailRows.d.ts +11 -0
  49. package/esm/components/intelligence/detail/IntelligenceDetailRows.d.ts.map +1 -0
  50. package/esm/components/intelligence/detail/IntelligenceDetailRows.js +16 -0
  51. package/esm/components/intelligence/detail/IntelligenceStepCarousel.d.ts +12 -0
  52. package/esm/components/intelligence/detail/IntelligenceStepCarousel.d.ts.map +1 -0
  53. package/esm/components/intelligence/detail/IntelligenceStepCarousel.js +21 -0
  54. package/esm/components/intelligence/format/formatDuration.d.ts +23 -0
  55. package/esm/components/intelligence/format/formatDuration.d.ts.map +1 -0
  56. package/esm/components/intelligence/format/formatDuration.js +38 -0
  57. package/esm/components/intelligence/format/formatTokens.d.ts +8 -0
  58. package/esm/components/intelligence/format/formatTokens.d.ts.map +1 -0
  59. package/esm/components/intelligence/format/formatTokens.js +10 -0
  60. package/esm/components/intelligence/format/resolveMessage.d.ts +9 -0
  61. package/esm/components/intelligence/format/resolveMessage.d.ts.map +1 -0
  62. package/esm/components/intelligence/format/resolveMessage.js +10 -0
  63. package/esm/components/intelligence/intelligenceBem.d.ts +7 -0
  64. package/esm/components/intelligence/intelligenceBem.d.ts.map +1 -0
  65. package/esm/components/intelligence/intelligenceBem.js +8 -0
  66. package/esm/components/intelligence/panel/IconCircle.d.ts +10 -0
  67. package/esm/components/intelligence/panel/IconCircle.d.ts.map +1 -0
  68. package/esm/components/intelligence/panel/IconCircle.js +12 -0
  69. package/esm/components/intelligence/panel/IntelligenceCategoryList.d.ts +13 -0
  70. package/esm/components/intelligence/panel/IntelligenceCategoryList.d.ts.map +1 -0
  71. package/esm/components/intelligence/panel/IntelligenceCategoryList.js +10 -0
  72. package/esm/components/intelligence/panel/IntelligenceCategoryRow.d.ts +14 -0
  73. package/esm/components/intelligence/panel/IntelligenceCategoryRow.d.ts.map +1 -0
  74. package/esm/components/intelligence/panel/IntelligenceCategoryRow.js +19 -0
  75. package/esm/components/intelligence/panel/IntelligenceDurationTokens.d.ts +12 -0
  76. package/esm/components/intelligence/panel/IntelligenceDurationTokens.d.ts.map +1 -0
  77. package/esm/components/intelligence/panel/IntelligenceDurationTokens.js +19 -0
  78. package/esm/components/intelligence/panel/IntelligenceFooter.d.ts +10 -0
  79. package/esm/components/intelligence/panel/IntelligenceFooter.d.ts.map +1 -0
  80. package/esm/components/intelligence/panel/IntelligenceFooter.js +20 -0
  81. package/esm/components/intelligence/panel/IntelligenceHeader.d.ts +8 -0
  82. package/esm/components/intelligence/panel/IntelligenceHeader.d.ts.map +1 -0
  83. package/esm/components/intelligence/panel/IntelligenceHeader.js +15 -0
  84. package/esm/components/intelligence/panel/IntelligenceStepTooltip.d.ts +12 -0
  85. package/esm/components/intelligence/panel/IntelligenceStepTooltip.d.ts.map +1 -0
  86. package/esm/components/intelligence/panel/IntelligenceStepTooltip.js +18 -0
  87. package/esm/components/intelligence/panel/IntelligenceTimeline.d.ts +15 -0
  88. package/esm/components/intelligence/panel/IntelligenceTimeline.d.ts.map +1 -0
  89. package/esm/components/intelligence/panel/IntelligenceTimeline.js +16 -0
  90. package/esm/components/intelligence/panel/TimelineBar.d.ts +19 -0
  91. package/esm/components/intelligence/panel/TimelineBar.d.ts.map +1 -0
  92. package/esm/components/intelligence/panel/TimelineBar.js +34 -0
  93. package/esm/components/messages/AssistantItem.d.ts.map +1 -1
  94. package/esm/components/messages/AssistantItem.js +15 -1
  95. package/esm/components/messages/AssistantItemFeedback.d.ts +6 -1
  96. package/esm/components/messages/AssistantItemFeedback.d.ts.map +1 -1
  97. package/esm/components/messages/AssistantItemFeedback.js +9 -2
  98. package/esm/components/messages/ConversationItemContents.d.ts.map +1 -1
  99. package/esm/components/messages/ConversationItemContents.js +1 -1
  100. package/esm/components/messages/conversationContents/ConversationDashboardContent.d.ts +2 -1
  101. package/esm/components/messages/conversationContents/ConversationDashboardContent.d.ts.map +1 -1
  102. package/esm/components/messages/conversationContents/ConversationDashboardContent.js +3 -2
  103. package/esm/context/dashboard.d.ts +2 -2
  104. package/esm/context/dashboard.d.ts.map +1 -1
  105. package/esm/context/dashboard.js +5 -2
  106. package/esm/hooks/useGenAIStore.d.ts +1 -0
  107. package/esm/hooks/useGenAIStore.d.ts.map +1 -1
  108. package/esm/hooks/useGenAIStore.js +3 -3
  109. package/esm/index.d.ts +2 -0
  110. package/esm/index.d.ts.map +1 -1
  111. package/esm/index.js +2 -0
  112. package/esm/localization/bundles/en-US.localization-bundle.d.ts +184 -0
  113. package/esm/localization/bundles/en-US.localization-bundle.d.ts.map +1 -1
  114. package/esm/localization/bundles/en-US.localization-bundle.js +184 -0
  115. package/esm/sdk-ui-gen-ai.d.ts +37 -0
  116. package/esm/store/chatWindow/chatWindowSelectors.d.ts +2 -0
  117. package/esm/store/chatWindow/chatWindowSelectors.d.ts.map +1 -1
  118. package/esm/store/chatWindow/chatWindowSelectors.js +5 -0
  119. package/esm/store/chatWindow/chatWindowSlice.d.ts +7 -1
  120. package/esm/store/chatWindow/chatWindowSlice.d.ts.map +1 -1
  121. package/esm/store/chatWindow/chatWindowSlice.js +3 -1
  122. package/esm/store/messages/messagesSelectors.d.ts +6 -0
  123. package/esm/store/messages/messagesSelectors.d.ts.map +1 -1
  124. package/esm/store/messages/messagesSelectors.js +9 -0
  125. package/esm/store/messages/messagesSlice.d.ts +11 -3
  126. package/esm/store/messages/messagesSlice.d.ts.map +1 -1
  127. package/esm/store/messages/messagesSlice.js +34 -1
  128. package/esm/store/sideEffects/onUserMessage.d.ts.map +1 -1
  129. package/esm/store/sideEffects/onUserMessage.js +40 -6
  130. package/esm/store/store.d.ts +1 -1
  131. package/esm/store/store.d.ts.map +1 -1
  132. package/esm/store/store.js +2 -2
  133. package/esm/types.d.ts +33 -1
  134. package/esm/types.d.ts.map +1 -1
  135. package/package.json +20 -20
  136. package/styles/css/intelligence.css +302 -0
  137. package/styles/css/intelligence.css.map +1 -0
  138. package/styles/css/main.css +306 -1
  139. package/styles/css/main.css.map +1 -1
  140. package/styles/css/messages.css +5 -1
  141. package/styles/css/messages.css.map +1 -1
  142. package/styles/scss/intelligence.scss +330 -0
  143. package/styles/scss/main.scss +1 -0
  144. package/styles/scss/messages.scss +10 -1
@@ -0,0 +1,23 @@
1
+ import type { IChatConversationResponseTrace } from "../../../types.js";
2
+ import type { IInteractionIntelligence, IInteractionIntelligenceTotals } from "./types.js";
3
+ /**
4
+ * Builds the render model for one response: a step carries timing/token spend but no category,
5
+ * the actions within it carry a category but no timing, and the two are joined here.
6
+ *
7
+ * Every action's category is kept, and time stays whole on the step's own tile — nothing is split
8
+ * or summed per category, so two categories highlighting the same tile is not double-counting. A
9
+ * category comes only from `detail.category`; an action without a `detail` yields no category row,
10
+ * though its step keeps its time regardless.
11
+ *
12
+ * A tile's `index` is its array position, not the backend's `stepIndex` — it is the highlight key.
13
+ * @internal
14
+ */
15
+ export declare function deriveInteractionIntelligenceFromSteps(responseId: string, trace: IChatConversationResponseTrace): IInteractionIntelligence;
16
+ /**
17
+ * The turn-level totals only, without resolving categories or building any detail rows — cheaper
18
+ * than {@link deriveInteractionIntelligenceFromSteps} for callers (e.g. the inline trigger) that
19
+ * only need the duration/token summary, and the single source of truth both callers share.
20
+ * @internal
21
+ */
22
+ export declare function deriveInteractionIntelligenceTotals(trace: IChatConversationResponseTrace): IInteractionIntelligenceTotals;
23
+ //# sourceMappingURL=deriveInteractionIntelligenceFromSteps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deriveInteractionIntelligenceFromSteps.d.ts","sourceRoot":"","sources":["../../../../src/components/intelligence/data/deriveInteractionIntelligenceFromSteps.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,mBAAmB,CAAC;AAIxE,OAAO,KAAK,EAIR,wBAAwB,EACxB,8BAA8B,EAEjC,MAAM,YAAY,CAAC;AAqBpB;;;;;;;;;;;GAWG;AACH,wBAAgB,sCAAsC,CAClD,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,8BAA8B,GACtC,wBAAwB,CAqD1B;AAED;;;;;GAKG;AACH,wBAAgB,mCAAmC,CAC/C,KAAK,EAAE,8BAA8B,GACtC,8BAA8B,CAYhC"}
@@ -0,0 +1,109 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { defineMessages } from "react-intl";
3
+ import { excerptForDetail, rowsForDetail } from "./categoryDetailRows.js";
4
+ import { mergeDetailRows } from "./mergeDetailRows.js";
5
+ /**
6
+ * i18n message id per known category. Typed by {@link GenAIInteractionStepCategory}, so adding a
7
+ * category to the backend's vocabulary fails to compile until its label is authored here.
8
+ */
9
+ const CATEGORY_LABEL_MESSAGES = defineMessages({
10
+ applyMemory: { id: "gd.gen-ai.interactionIntelligence.category.applyMemory" },
11
+ catalogSearch: { id: "gd.gen-ai.interactionIntelligence.category.catalogSearch" },
12
+ knowledgeSearch: { id: "gd.gen-ai.interactionIntelligence.category.knowledgeSearch" },
13
+ skillRouting: { id: "gd.gen-ai.interactionIntelligence.category.skillRouting" },
14
+ metricQuery: { id: "gd.gen-ai.interactionIntelligence.category.metricQuery" },
15
+ composeAnswer: { id: "gd.gen-ai.interactionIntelligence.category.composeAnswer" },
16
+ });
17
+ /**
18
+ * Builds the render model for one response: a step carries timing/token spend but no category,
19
+ * the actions within it carry a category but no timing, and the two are joined here.
20
+ *
21
+ * Every action's category is kept, and time stays whole on the step's own tile — nothing is split
22
+ * or summed per category, so two categories highlighting the same tile is not double-counting. A
23
+ * category comes only from `detail.category`; an action without a `detail` yields no category row,
24
+ * though its step keeps its time regardless.
25
+ *
26
+ * A tile's `index` is its array position, not the backend's `stepIndex` — it is the highlight key.
27
+ * @internal
28
+ */
29
+ export function deriveInteractionIntelligenceFromSteps(responseId, trace) {
30
+ const sorted = [...trace.steps].sort((a, b) => a.stepIndex - b.stepIndex);
31
+ const byCategory = new Map();
32
+ const steps = sorted.map((step, index) => {
33
+ const actions = trace.detailsByStepId[step.stepId] ?? [];
34
+ const categoriesInStep = [];
35
+ for (const { detail } of actions) {
36
+ if (!detail) {
37
+ continue;
38
+ }
39
+ const { category } = detail;
40
+ if (!categoriesInStep.includes(category)) {
41
+ categoriesInStep.push(category);
42
+ }
43
+ const entry = byCategory.get(category) ?? { stepIndexes: [], details: [] };
44
+ byCategory.set(category, entry);
45
+ if (entry.stepIndexes.at(-1) !== index) {
46
+ entry.stepIndexes.push(index);
47
+ }
48
+ entry.details.push(detail);
49
+ }
50
+ return {
51
+ stepId: step.stepId,
52
+ index,
53
+ durationMs: step.durationMs,
54
+ tokens: step.tokens.total,
55
+ categories: categoriesInStep,
56
+ };
57
+ });
58
+ // Map insertion order already reflects each category's earliest step, since steps are
59
+ // walked in order above.
60
+ const categories = Array.from(byCategory.entries()).map(([category, entry]) => ({
61
+ category,
62
+ // Absent for a category newer than this client, which `resolveMessage` then renders as
63
+ // the raw category id rather than failing.
64
+ labelId: CATEGORY_LABEL_MESSAGES[category]?.id,
65
+ stepIndexes: entry.stepIndexes,
66
+ excerpt: excerptFor(entry.details),
67
+ detailRows: mergeDetailRows(entry.details.map(rowsForDetail)),
68
+ }));
69
+ return {
70
+ responseId,
71
+ totals: deriveInteractionIntelligenceTotals(trace),
72
+ steps,
73
+ categories,
74
+ traceId: trace.traceId,
75
+ };
76
+ }
77
+ /**
78
+ * The turn-level totals only, without resolving categories or building any detail rows — cheaper
79
+ * than {@link deriveInteractionIntelligenceFromSteps} for callers (e.g. the inline trigger) that
80
+ * only need the duration/token summary, and the single source of truth both callers share.
81
+ * @internal
82
+ */
83
+ export function deriveInteractionIntelligenceTotals(trace) {
84
+ const reported = trace.steps.filter((step) => step.tokens.total !== undefined);
85
+ return {
86
+ stepsCount: trace.steps.length,
87
+ durationMs: trace.steps.reduce((sum, step) => sum + step.durationMs, 0),
88
+ // Left undefined when no step reported a token total, so "unknown" never renders as "0".
89
+ tokens: reported.length === 0
90
+ ? undefined
91
+ : reported.reduce((sum, step) => sum + (step.tokens.total ?? 0), 0),
92
+ };
93
+ }
94
+ /**
95
+ * Merges a category's per-occurrence excerpt fragments by `id`, summing `count` values — so a
96
+ * category that ran across several steps shows one combined "5 found" rather than
97
+ * "2 found 3 found".
98
+ */
99
+ function excerptFor(details) {
100
+ const merged = new Map();
101
+ for (const fragment of details.flatMap(excerptForDetail)) {
102
+ const previousCount = merged.get(fragment.id)?.values?.["count"];
103
+ const count = fragment.values?.["count"];
104
+ merged.set(fragment.id, typeof previousCount === "number" && typeof count === "number"
105
+ ? { id: fragment.id, values: { ...fragment.values, count: previousCount + count } }
106
+ : fragment);
107
+ }
108
+ return merged.size === 0 ? undefined : { fragments: Array.from(merged.values()) };
109
+ }
@@ -0,0 +1,15 @@
1
+ import type { IInteractionStepDetailRow } from "./types.js";
2
+ /**
3
+ * Merges several occurrences' `detailRows` into one flat set for a category, grouping by
4
+ * `labelId`. A label appearing in only one occurrence keeps that row untouched. A label repeated
5
+ * across occurrences merges by value kind:
6
+ * - `list` rows concatenate `items` (and sum `truncatedCount`), keeping the first occurrence
7
+ * that authors a `heading`/`bulleted`.
8
+ * - `groups` rows concatenate the `groups` arrays across occurrences.
9
+ * - `text` rows become a plain, unheaded `list` of each occurrence's text as one item — the same
10
+ * "stacked plain list" look already used for single-occurrence match lists like "Used".
11
+ * - Mismatched kinds across occurrences fall back to the first occurrence's row.
12
+ * @internal
13
+ */
14
+ export declare function mergeDetailRows(rowGroups: IInteractionStepDetailRow[][]): IInteractionStepDetailRow[];
15
+ //# sourceMappingURL=mergeDetailRows.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mergeDetailRows.d.ts","sourceRoot":"","sources":["../../../../src/components/intelligence/data/mergeDetailRows.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAA8B,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAExF;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,yBAAyB,EAAE,EAAE,GAAG,yBAAyB,EAAE,CAcrG"}
@@ -0,0 +1,91 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { MAX_DETAIL_LIST_ITEMS } from "./constants.js";
3
+ /**
4
+ * Merges several occurrences' `detailRows` into one flat set for a category, grouping by
5
+ * `labelId`. A label appearing in only one occurrence keeps that row untouched. A label repeated
6
+ * across occurrences merges by value kind:
7
+ * - `list` rows concatenate `items` (and sum `truncatedCount`), keeping the first occurrence
8
+ * that authors a `heading`/`bulleted`.
9
+ * - `groups` rows concatenate the `groups` arrays across occurrences.
10
+ * - `text` rows become a plain, unheaded `list` of each occurrence's text as one item — the same
11
+ * "stacked plain list" look already used for single-occurrence match lists like "Used".
12
+ * - Mismatched kinds across occurrences fall back to the first occurrence's row.
13
+ * @internal
14
+ */
15
+ export function mergeDetailRows(rowGroups) {
16
+ // Map keeps first-seen label order, which is the order the rows render in.
17
+ const rowsByLabel = new Map();
18
+ for (const row of rowGroups.flat()) {
19
+ const existing = rowsByLabel.get(row.labelId);
20
+ if (existing) {
21
+ existing.push(row);
22
+ }
23
+ else {
24
+ rowsByLabel.set(row.labelId, [row]);
25
+ }
26
+ }
27
+ return Array.from(rowsByLabel.values(), mergeRowGroup);
28
+ }
29
+ function mergeRowGroup(rows) {
30
+ const [first] = rows;
31
+ if (rows.length === 1) {
32
+ return first;
33
+ }
34
+ const kinds = new Set(rows.map((row) => row.value.kind));
35
+ if (kinds.size === 1 && kinds.has("list")) {
36
+ const lists = rows.flatMap((row) => (row.value.kind === "list" ? [row.value] : []));
37
+ const headingSource = lists.find((list) => list.heading) ?? lists[0];
38
+ const merged = mergeLists(lists);
39
+ const totalCount = merged.items.length + (merged.truncatedCount ?? 0);
40
+ return {
41
+ labelId: first.labelId,
42
+ value: {
43
+ kind: "list",
44
+ heading: headingSource.heading,
45
+ // Carried over, or the heading renders as its raw message id. Its `count` is
46
+ // restated from the merged total so it describes every occurrence, not the first.
47
+ headingId: headingSource.headingId,
48
+ headingValues: headingSource.headingValues
49
+ ? { ...headingSource.headingValues, count: totalCount }
50
+ : undefined,
51
+ bulleted: headingSource.bulleted,
52
+ ...merged,
53
+ },
54
+ };
55
+ }
56
+ if (kinds.size === 1 && kinds.has("groups")) {
57
+ return {
58
+ labelId: first.labelId,
59
+ value: {
60
+ kind: "groups",
61
+ groups: rows.flatMap((row) => (row.value.kind === "groups" ? row.value.groups : [])),
62
+ },
63
+ };
64
+ }
65
+ if (kinds.size === 1 && kinds.has("text")) {
66
+ return {
67
+ labelId: first.labelId,
68
+ value: {
69
+ kind: "list",
70
+ bulleted: false,
71
+ items: rows.map((row) => ({ label: row.value.kind === "text" ? row.value.text : "" })),
72
+ },
73
+ };
74
+ }
75
+ // Mismatched value kinds across occurrences: fall back to the first occurrence's row.
76
+ return first;
77
+ }
78
+ /**
79
+ * Concatenates several lists' `items` and sums their `truncatedCount`s, re-applying the per-list
80
+ * cap to the result — each occurrence was capped on its own, so concatenating N of them would
81
+ * otherwise render N times the intended number of items.
82
+ */
83
+ function mergeLists(lists) {
84
+ const items = lists.flatMap((list) => list.items);
85
+ const alreadyTruncated = lists.reduce((sum, list) => sum + (list.truncatedCount ?? 0), 0);
86
+ const overflow = Math.max(items.length - MAX_DETAIL_LIST_ITEMS, 0);
87
+ return {
88
+ items: overflow === 0 ? items : items.slice(0, MAX_DETAIL_LIST_ITEMS),
89
+ truncatedCount: alreadyTruncated + overflow || undefined,
90
+ };
91
+ }
@@ -0,0 +1,18 @@
1
+ import type { IInteractionStepTile, ITimelineSegment } from "./types.js";
2
+ /**
3
+ * Floor applied to a segment's share of the track, in percent, before the widths are normalized
4
+ * back to 100% — so a very short step (e.g. 0.1s within a 13.6s turn) keeps a visible sliver
5
+ * instead of collapsing next to the long ones. It is a floor on the raw share, not a guarantee
6
+ * about the final width: past 100 segments the normalization has to scale everything below it,
7
+ * since 100 x 1% already fills the track.
8
+ * @internal
9
+ */
10
+ export declare const MIN_SEGMENT_WIDTH_PCT = 1;
11
+ /**
12
+ * Computes one segment per step, tiling the entire turn `[0, totalMs]` in step order. Both the
13
+ * list view and every category's detail view render this same sequence, highlighting only the
14
+ * tile(s) at the step indexes relevant to what's currently shown.
15
+ * @internal
16
+ */
17
+ export declare function computeSegments(steps: IInteractionStepTile[], totalMs: number): ITimelineSegment[];
18
+ //# sourceMappingURL=timeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../../../../src/components/intelligence/data/timeline.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEzE;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAalG"}
@@ -0,0 +1,40 @@
1
+ // (C) 2026 GoodData Corporation
2
+ /**
3
+ * Floor applied to a segment's share of the track, in percent, before the widths are normalized
4
+ * back to 100% — so a very short step (e.g. 0.1s within a 13.6s turn) keeps a visible sliver
5
+ * instead of collapsing next to the long ones. It is a floor on the raw share, not a guarantee
6
+ * about the final width: past 100 segments the normalization has to scale everything below it,
7
+ * since 100 x 1% already fills the track.
8
+ * @internal
9
+ */
10
+ export const MIN_SEGMENT_WIDTH_PCT = 1;
11
+ /**
12
+ * Computes one segment per step, tiling the entire turn `[0, totalMs]` in step order. Both the
13
+ * list view and every category's detail view render this same sequence, highlighting only the
14
+ * tile(s) at the step indexes relevant to what's currently shown.
15
+ * @internal
16
+ */
17
+ export function computeSegments(steps, totalMs) {
18
+ if (totalMs <= 0) {
19
+ return [];
20
+ }
21
+ const rawWidths = steps.map((step) => Math.max((step.durationMs / totalMs) * 100, MIN_SEGMENT_WIDTH_PCT));
22
+ const widths = normalizeToTotal(rawWidths, 100);
23
+ return steps.map((step, index) => ({
24
+ stepId: step.stepId,
25
+ stepIndex: step.index,
26
+ widthPct: widths[index],
27
+ }));
28
+ }
29
+ /**
30
+ * Rescales a list of positive weights so they sum exactly to `target`, preserving their
31
+ * relative proportions. Used so segment widths tile the track exactly to 100% even after the
32
+ * per-segment minimum-width floor would otherwise make them overshoot or undershoot it.
33
+ */
34
+ function normalizeToTotal(weights, target) {
35
+ const sum = weights.reduce((total, weight) => total + weight, 0);
36
+ if (sum <= 0) {
37
+ return weights;
38
+ }
39
+ return weights.map((weight) => (weight / sum) * target);
40
+ }
@@ -0,0 +1,159 @@
1
+ import type { GenAIInteractionStepCategory } from "@gooddata/sdk-backend-spi";
2
+ /**
3
+ * One item within a {@link IInteractionStepDetailList}. `meta` is a translated suffix shown
4
+ * dimmed after the item (e.g. a match percentage).
5
+ * @internal
6
+ */
7
+ export interface IInteractionStepDetailListItem {
8
+ label: string;
9
+ meta?: {
10
+ id: string;
11
+ values?: Record<string, string | number>;
12
+ };
13
+ }
14
+ /**
15
+ * A count + list of items, optionally truncated, with `truncatedCount` covering the rest (e.g.
16
+ * "100 memories" showing 3 items plus "...and 97 more"). `heading` is the count line (e.g. "3
17
+ * files", "4 skills"); omit it to render the list with no count line above it (e.g. a plain
18
+ * stacked "Used" list).
19
+ * @internal
20
+ */
21
+ export interface IInteractionStepDetailList {
22
+ /**
23
+ * i18n message id for `heading`, when it is one of a known/stable set (interpolated with
24
+ * `headingValues`). Falls back to rendering `heading` verbatim when absent.
25
+ */
26
+ headingId?: string;
27
+ /** Values to interpolate into `headingId`'s message, e.g. `{ count: 4 }`. */
28
+ headingValues?: Record<string, string | number>;
29
+ heading?: string;
30
+ items: IInteractionStepDetailListItem[];
31
+ /** Number of further items not included in `items`, when the list was truncated. */
32
+ truncatedCount?: number;
33
+ /** Renders without bullet markers — used for "Used" style single-column match lists. */
34
+ bulleted?: boolean;
35
+ }
36
+ /**
37
+ * One key/value row shown in a category's detail view. The value is one of: plain text, a
38
+ * single count+list, or several independent count+lists stacked under one label (e.g. Catalogue
39
+ * search's "Query" row: a metrics list and a date-dimensions list together).
40
+ * @internal
41
+ */
42
+ export interface IInteractionStepDetailRow {
43
+ /** i18n message id for the row's label — every row label is authored, so always resolvable. */
44
+ labelId: string;
45
+ value: IInteractionStepDetailRowValue;
46
+ }
47
+ /**
48
+ * @internal
49
+ */
50
+ export type IInteractionStepDetailRowValue = {
51
+ kind: "text";
52
+ /**
53
+ * i18n message id for `text`, when it is one of a known/stable set. Falls back to
54
+ * rendering `text` verbatim when absent.
55
+ */
56
+ textId?: string;
57
+ text: string;
58
+ } | ({
59
+ kind: "list";
60
+ } & IInteractionStepDetailList) | {
61
+ kind: "groups";
62
+ groups: IInteractionStepDetailList[];
63
+ };
64
+ /**
65
+ * One fragment of a category row's right-hand excerpt (e.g. "2 found", "1 used"). `id` is
66
+ * always authored, so an excerpt is always fully translatable.
67
+ * @internal
68
+ */
69
+ export interface IInteractionCategoryExcerptFragment {
70
+ id: string;
71
+ values?: Record<string, string | number>;
72
+ }
73
+ /**
74
+ * The short right-hand summary shown on a category's list row, e.g. "110 memories", or the two
75
+ * fragments "2 found" + "1 used" for Catalogue search. Absent renders no excerpt.
76
+ * @internal
77
+ */
78
+ export interface IInteractionCategoryExcerpt {
79
+ fragments: IInteractionCategoryExcerptFragment[];
80
+ }
81
+ /**
82
+ * One interaction step: the only carrier of time and token spend, and one tile of the shared
83
+ * timeline. `categories` lists the categories that ran within it, in first-seen order — empty
84
+ * when none of its actions carried a recognized category, in which case the tile still keeps
85
+ * its own time on the timeline but produces no category row.
86
+ * @internal
87
+ */
88
+ export interface IInteractionStepTile {
89
+ stepId: string;
90
+ /** This tile's position in {@link IInteractionIntelligence.steps} — the highlight key. */
91
+ index: number;
92
+ durationMs: number;
93
+ tokens?: number;
94
+ categories: GenAIInteractionStepCategory[];
95
+ }
96
+ /**
97
+ * One category: a list row and a detail page. Never carries a duration or token count — time and
98
+ * tokens live only on {@link IInteractionStepTile}, which this only references by index.
99
+ * @internal
100
+ */
101
+ export interface IInteractionCategory {
102
+ /**
103
+ * The category as the backend sends it. Maps to an icon with a safe default, and is the
104
+ * display fallback when `labelId` is absent.
105
+ */
106
+ category: GenAIInteractionStepCategory;
107
+ /** i18n message id for the category's display label, when it is one of a known/stable set. */
108
+ labelId?: string;
109
+ /** Indexes into {@link IInteractionIntelligence.steps} where this category occurred. */
110
+ stepIndexes: number[];
111
+ /** The list row's right-hand summary. Absent renders no excerpt. */
112
+ excerpt?: IInteractionCategoryExcerpt;
113
+ /**
114
+ * Detail rows merged across every occurrence of this category, by grouping on row label: a
115
+ * label appearing in more than one occurrence gets one row whose list items/groups are
116
+ * concatenated in occurrence order (a repeated text value becomes a plain stacked list).
117
+ */
118
+ detailRows: IInteractionStepDetailRow[];
119
+ }
120
+ /**
121
+ * Turn-level totals, computed straight from the steps — independent of category resolution.
122
+ * @internal
123
+ */
124
+ export interface IInteractionIntelligenceTotals {
125
+ stepsCount: number;
126
+ durationMs: number;
127
+ tokens?: number;
128
+ }
129
+ /**
130
+ * The full render model for one assistant response's Interaction Intelligence data.
131
+ * @internal
132
+ */
133
+ export interface IInteractionIntelligence {
134
+ responseId: string;
135
+ totals: IInteractionIntelligenceTotals;
136
+ /** The timeline, in step order. */
137
+ steps: IInteractionStepTile[];
138
+ /** The list rows, ordered by each category's earliest step. */
139
+ categories: IInteractionCategory[];
140
+ /** Backend trace id of this response, for support/debugging. Absent when never received. */
141
+ traceId?: string;
142
+ }
143
+ /**
144
+ * One tile of the shared timeline track: a single step's slot in the turn, sized proportionally
145
+ * to its duration.
146
+ * @internal
147
+ */
148
+ export interface ITimelineSegment {
149
+ stepId: string;
150
+ /** Index into {@link IInteractionIntelligence.steps} — the highlight key. */
151
+ stepIndex: number;
152
+ widthPct: number;
153
+ }
154
+ /**
155
+ * The modes the panel can render: the full list/timeline view or a single category's detail view.
156
+ * @public
157
+ */
158
+ export type InteractionIntelligenceMode = "list" | "detail";
159
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/components/intelligence/data/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ // (C) 2026 GoodData Corporation
2
+ export {};
@@ -0,0 +1,9 @@
1
+ import type { IInteractionIntelligence } from "./types.js";
2
+ /**
3
+ * The Interaction Intelligence render model for one response, or `undefined` when `enabled` is
4
+ * false or the response has no trace. Nothing is fetched — the trace streamed in with the
5
+ * response's own messages and is read straight out of the store.
6
+ * @internal
7
+ */
8
+ export declare function useInteractionIntelligenceData(responseId: string, enabled: boolean): IInteractionIntelligence | undefined;
9
+ //# sourceMappingURL=useInteractionIntelligenceData.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useInteractionIntelligenceData.d.ts","sourceRoot":"","sources":["../../../../src/components/intelligence/data/useInteractionIntelligenceData.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE3D;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAC1C,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,GACjB,wBAAwB,GAAG,SAAS,CAWtC"}
@@ -0,0 +1,20 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { useMemo } from "react";
3
+ import { useSelector } from "react-redux";
4
+ import { interactionTraceByResponseIdSelector } from "../../../store/messages/messagesSelectors.js";
5
+ import { deriveInteractionIntelligenceFromSteps } from "./deriveInteractionIntelligenceFromSteps.js";
6
+ /**
7
+ * The Interaction Intelligence render model for one response, or `undefined` when `enabled` is
8
+ * false or the response has no trace. Nothing is fetched — the trace streamed in with the
9
+ * response's own messages and is read straight out of the store.
10
+ * @internal
11
+ */
12
+ export function useInteractionIntelligenceData(responseId, enabled) {
13
+ const trace = useSelector((state) => interactionTraceByResponseIdSelector(state, responseId));
14
+ return useMemo(() => {
15
+ if (!enabled || trace.steps.length === 0) {
16
+ return undefined;
17
+ }
18
+ return deriveInteractionIntelligenceFromSteps(responseId, trace);
19
+ }, [responseId, enabled, trace]);
20
+ }
@@ -0,0 +1,9 @@
1
+ import type { IInteractionIntelligenceTotals } from "./types.js";
2
+ /**
3
+ * The turn-level totals (duration/tokens) for one response, read straight out of the stored
4
+ * interaction steps without resolving categories or building any detail rows. `undefined` until
5
+ * the response has steps — the inline trigger renders nothing until then.
6
+ * @internal
7
+ */
8
+ export declare function useInteractionIntelligenceTotals(responseId: string): IInteractionIntelligenceTotals | undefined;
9
+ //# sourceMappingURL=useInteractionIntelligenceTotals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useInteractionIntelligenceTotals.d.ts","sourceRoot":"","sources":["../../../../src/components/intelligence/data/useInteractionIntelligenceTotals.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,YAAY,CAAC;AAEjE;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAC5C,UAAU,EAAE,MAAM,GACnB,8BAA8B,GAAG,SAAS,CAW5C"}
@@ -0,0 +1,20 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { useMemo } from "react";
3
+ import { useSelector } from "react-redux";
4
+ import { interactionTraceByResponseIdSelector } from "../../../store/messages/messagesSelectors.js";
5
+ import { deriveInteractionIntelligenceTotals } from "./deriveInteractionIntelligenceFromSteps.js";
6
+ /**
7
+ * The turn-level totals (duration/tokens) for one response, read straight out of the stored
8
+ * interaction steps without resolving categories or building any detail rows. `undefined` until
9
+ * the response has steps — the inline trigger renders nothing until then.
10
+ * @internal
11
+ */
12
+ export function useInteractionIntelligenceTotals(responseId) {
13
+ const trace = useSelector((state) => interactionTraceByResponseIdSelector(state, responseId));
14
+ return useMemo(() => {
15
+ if (!responseId || trace.steps.length === 0) {
16
+ return undefined;
17
+ }
18
+ return deriveInteractionIntelligenceTotals(trace);
19
+ }, [responseId, trace]);
20
+ }
@@ -0,0 +1,10 @@
1
+ import type { IInteractionStepDetailRowValue } from "../data/types.js";
2
+ export interface IDetailRowValueProps {
3
+ value: IInteractionStepDetailRowValue;
4
+ }
5
+ /**
6
+ * Renders one detail-row value generically — text, a single count+list, or several independent
7
+ * count+lists stacked together.
8
+ */
9
+ export declare function DetailRowValue({ value }: IDetailRowValueProps): string | import("react/jsx-runtime").JSX.Element;
10
+ //# sourceMappingURL=DetailRowValue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DetailRowValue.d.ts","sourceRoot":"","sources":["../../../../src/components/intelligence/detail/DetailRowValue.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAA8B,8BAA8B,EAAE,MAAM,kBAAkB,CAAC;AA8CnG,MAAM,WAAW,oBAAoB;IACjC,KAAK,EAAE,8BAA8B,CAAC;CACzC;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,EAAE,oBAAoB,oDAgB7D"}
@@ -0,0 +1,23 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // (C) 2026 GoodData Corporation
3
+ import { useIntl } from "react-intl";
4
+ import { resolveMessage } from "../format/resolveMessage.js";
5
+ import { e } from "../intelligenceBem.js";
6
+ function DetailList({ heading, headingId, headingValues, items, truncatedCount, bulleted = true, }) {
7
+ const intl = useIntl();
8
+ return (_jsxs("div", { className: e("detail-rows__list"), children: [heading ? (_jsx("div", { className: e("detail-rows__list-heading"), children: resolveMessage(intl, headingId, heading, headingValues) })) : null, _jsx("ul", { className: e("detail-rows__list-items", { plain: !bulleted }), children: items.map((item, index) => (_jsxs("li", { children: [item.label, item.meta ? (_jsxs("span", { className: e("detail-rows__list-item-meta"), children: [" ", intl.formatMessage({ id: item.meta.id }, item.meta.values)] })) : null] }, index))) }), truncatedCount ? (_jsx("div", { className: e("detail-rows__list-truncated"), children: intl.formatMessage({ id: "gd.gen-ai.interactionIntelligence.detail.andMore" }, { count: truncatedCount }) })) : null] }));
9
+ }
10
+ /**
11
+ * Renders one detail-row value generically — text, a single count+list, or several independent
12
+ * count+lists stacked together.
13
+ */
14
+ export function DetailRowValue({ value }) {
15
+ const intl = useIntl();
16
+ if (value.kind === "text") {
17
+ return resolveMessage(intl, value.textId, value.text);
18
+ }
19
+ if (value.kind === "list") {
20
+ return _jsx(DetailList, { ...value });
21
+ }
22
+ return (_jsx(_Fragment, { children: value.groups.map((group, index) => (_jsx(DetailList, { ...group }, index))) }));
23
+ }
@@ -0,0 +1,17 @@
1
+ import type { IInteractionIntelligence } from "../data/types.js";
2
+ export interface IIntelligenceCategoryDetailProps {
3
+ data: IInteractionIntelligence;
4
+ /** Index of the category to show, within `data.categories`. */
5
+ categoryIndex: number;
6
+ onBack: () => void;
7
+ onClose: () => void;
8
+ onSelectCategory: (index: number) => void;
9
+ }
10
+ /**
11
+ * The detail view for a single category: header, the shared timeline with this category's own
12
+ * steps highlighted, its detail rows concatenated across every occurrence, and a carousel to
13
+ * move between categories. A pure function of `(data, categoryIndex)` — mountable directly at
14
+ * any index, without going through the list first.
15
+ */
16
+ export declare function IntelligenceCategoryDetail({ data, categoryIndex, onBack, onClose, onSelectCategory }: IIntelligenceCategoryDetailProps): import("react/jsx-runtime").JSX.Element | null;
17
+ //# sourceMappingURL=IntelligenceCategoryDetail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IntelligenceCategoryDetail.d.ts","sourceRoot":"","sources":["../../../../src/components/intelligence/detail/IntelligenceCategoryDetail.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAQjE,MAAM,WAAW,gCAAgC;IAC7C,IAAI,EAAE,wBAAwB,CAAC;IAC/B,+DAA+D;IAC/D,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,EACvC,IAAI,EACJ,aAAa,EACb,MAAM,EACN,OAAO,EACP,gBAAgB,EACnB,EAAE,gCAAgC,kDAyBlC"}
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { e } from "../intelligenceBem.js";
3
+ import { IntelligenceTimeline } from "../panel/IntelligenceTimeline.js";
4
+ import { IntelligenceDetailHeader } from "./IntelligenceDetailHeader.js";
5
+ import { IntelligenceDetailRows } from "./IntelligenceDetailRows.js";
6
+ import { IntelligenceStepCarousel } from "./IntelligenceStepCarousel.js";
7
+ /**
8
+ * The detail view for a single category: header, the shared timeline with this category's own
9
+ * steps highlighted, its detail rows concatenated across every occurrence, and a carousel to
10
+ * move between categories. A pure function of `(data, categoryIndex)` — mountable directly at
11
+ * any index, without going through the list first.
12
+ */
13
+ export function IntelligenceCategoryDetail({ data, categoryIndex, onBack, onClose, onSelectCategory, }) {
14
+ const category = data.categories[categoryIndex];
15
+ if (!category) {
16
+ return null;
17
+ }
18
+ return (_jsxs("div", { className: e("detail"), children: [
19
+ _jsx(IntelligenceDetailHeader, { category: category, onBack: onBack, onClose: onClose }), _jsx(IntelligenceTimeline, { steps: data.steps, totalDurationMs: data.totals.durationMs, categories: data.categories, highlightedStepIndexes: category.stepIndexes }), _jsx(IntelligenceDetailRows, { rows: category.detailRows }), _jsx("div", { className: e("detail__divider") }), _jsx(IntelligenceStepCarousel, { currentIndex: categoryIndex, totalCount: data.categories.length, onSelectCategory: onSelectCategory })
20
+ ] }));
21
+ }