@finos/legend-lego 2.0.196 → 2.0.198

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 (71) hide show
  1. package/lib/code-editor/CodeEditor.d.ts.map +1 -1
  2. package/lib/code-editor/CodeEditor.js +14 -1
  3. package/lib/code-editor/CodeEditor.js.map +1 -1
  4. package/lib/index.css +2 -2
  5. package/lib/index.css.map +1 -1
  6. package/lib/legend-ai/LegendAIDocEnrichment.d.ts +60 -0
  7. package/lib/legend-ai/LegendAIDocEnrichment.d.ts.map +1 -0
  8. package/lib/legend-ai/LegendAIDocEnrichment.js +429 -0
  9. package/lib/legend-ai/LegendAIDocEnrichment.js.map +1 -0
  10. package/lib/legend-ai/LegendAITypes.d.ts +127 -1
  11. package/lib/legend-ai/LegendAITypes.d.ts.map +1 -1
  12. package/lib/legend-ai/LegendAITypes.js +111 -2
  13. package/lib/legend-ai/LegendAITypes.js.map +1 -1
  14. package/lib/legend-ai/LegendAI_LegendApplicationPlugin_Extension.d.ts +14 -1
  15. package/lib/legend-ai/LegendAI_LegendApplicationPlugin_Extension.d.ts.map +1 -1
  16. package/lib/legend-ai/LegendAI_LegendApplicationPlugin_Extension.js.map +1 -1
  17. package/lib/legend-ai/__test-utils__/LegendAITestUtils.d.ts +2 -1
  18. package/lib/legend-ai/__test-utils__/LegendAITestUtils.d.ts.map +1 -1
  19. package/lib/legend-ai/__test-utils__/LegendAITestUtils.js +37 -2
  20. package/lib/legend-ai/__test-utils__/LegendAITestUtils.js.map +1 -1
  21. package/lib/legend-ai/components/LegendAIAnalysisPanel.d.ts.map +1 -1
  22. package/lib/legend-ai/components/LegendAIAnalysisPanel.js +11 -12
  23. package/lib/legend-ai/components/LegendAIAnalysisPanel.js.map +1 -1
  24. package/lib/legend-ai/components/LegendAIAnalysisUtils.d.ts +7 -0
  25. package/lib/legend-ai/components/LegendAIAnalysisUtils.d.ts.map +1 -1
  26. package/lib/legend-ai/components/LegendAIAnalysisUtils.js +106 -41
  27. package/lib/legend-ai/components/LegendAIAnalysisUtils.js.map +1 -1
  28. package/lib/legend-ai/components/LegendAIChat.d.ts +1 -5
  29. package/lib/legend-ai/components/LegendAIChat.d.ts.map +1 -1
  30. package/lib/legend-ai/components/LegendAIChat.js +168 -109
  31. package/lib/legend-ai/components/LegendAIChat.js.map +1 -1
  32. package/lib/legend-ai/components/LegendAIChatHelpers.d.ts +21 -0
  33. package/lib/legend-ai/components/LegendAIChatHelpers.d.ts.map +1 -0
  34. package/lib/legend-ai/components/LegendAIChatHelpers.js +85 -0
  35. package/lib/legend-ai/components/LegendAIChatHelpers.js.map +1 -0
  36. package/lib/legend-ai/components/LegendAIChatInput.d.ts +21 -0
  37. package/lib/legend-ai/components/LegendAIChatInput.d.ts.map +1 -0
  38. package/lib/legend-ai/components/LegendAIChatInput.js +78 -0
  39. package/lib/legend-ai/components/LegendAIChatInput.js.map +1 -0
  40. package/lib/legend-ai/components/LegendAIScopeSelector.d.ts +25 -0
  41. package/lib/legend-ai/components/LegendAIScopeSelector.d.ts.map +1 -0
  42. package/lib/legend-ai/components/LegendAIScopeSelector.js +85 -0
  43. package/lib/legend-ai/components/LegendAIScopeSelector.js.map +1 -0
  44. package/lib/legend-ai/index.d.ts +8 -3
  45. package/lib/legend-ai/index.d.ts.map +1 -1
  46. package/lib/legend-ai/index.js +8 -3
  47. package/lib/legend-ai/index.js.map +1 -1
  48. package/lib/legend-ai/stores/LegendAIChatProcessors.d.ts +105 -0
  49. package/lib/legend-ai/stores/LegendAIChatProcessors.d.ts.map +1 -0
  50. package/lib/legend-ai/stores/LegendAIChatProcessors.js +1482 -0
  51. package/lib/legend-ai/stores/LegendAIChatProcessors.js.map +1 -0
  52. package/lib/legend-ai/stores/LegendAIChatState.d.ts +2 -35
  53. package/lib/legend-ai/stores/LegendAIChatState.d.ts.map +1 -1
  54. package/lib/legend-ai/stores/LegendAIChatState.js +114 -949
  55. package/lib/legend-ai/stores/LegendAIChatState.js.map +1 -1
  56. package/package.json +5 -5
  57. package/src/code-editor/CodeEditor.tsx +19 -0
  58. package/src/legend-ai/LegendAIDocEnrichment.ts +572 -0
  59. package/src/legend-ai/LegendAITypes.ts +213 -5
  60. package/src/legend-ai/LegendAI_LegendApplicationPlugin_Extension.ts +25 -0
  61. package/src/legend-ai/__test-utils__/LegendAITestUtils.ts +55 -1
  62. package/src/legend-ai/components/LegendAIAnalysisPanel.tsx +14 -34
  63. package/src/legend-ai/components/LegendAIAnalysisUtils.ts +157 -47
  64. package/src/legend-ai/components/LegendAIChat.tsx +389 -206
  65. package/src/legend-ai/components/LegendAIChatHelpers.ts +117 -0
  66. package/src/legend-ai/components/LegendAIChatInput.tsx +209 -0
  67. package/src/legend-ai/components/LegendAIScopeSelector.tsx +199 -0
  68. package/src/legend-ai/index.ts +31 -4
  69. package/src/legend-ai/stores/LegendAIChatProcessors.ts +2563 -0
  70. package/src/legend-ai/stores/LegendAIChatState.ts +161 -1697
  71. package/tsconfig.json +5 -0
@@ -20,7 +20,7 @@ import {
20
20
  LegendAIChartType,
21
21
  } from '../LegendAI_LegendApplicationPlugin_Extension.js';
22
22
  import type { LegendAIGridData } from '../LegendAITypes.js';
23
- import { isNonNullable, isNumber, isString } from '@finos/legend-shared';
23
+ import { isNumber, isString } from '@finos/legend-shared';
24
24
 
25
25
  const CHART_PALETTE_COUNT = 10;
26
26
  const MAX_CHART_ITEMS = 10;
@@ -36,10 +36,17 @@ interface ColumnProfile {
36
36
  isNumeric: boolean;
37
37
  isString: boolean;
38
38
  uniqueCount: number;
39
- values: unknown[];
39
+ nonNullCount: number;
40
40
  numericValues: number[];
41
41
  }
42
42
 
43
+ export interface LegendAIGridAnalysis {
44
+ metrics: LegendAIKeyMetric[];
45
+ chartType: LegendAIChartType;
46
+ chartData: LegendAIChartDataPoint[];
47
+ numericColumnName: string | undefined;
48
+ }
49
+
43
50
  function profileColumns(gridData: LegendAIGridData): ColumnProfile[] {
44
51
  const rows =
45
52
  gridData.rowData.length > MAX_PROFILE_SAMPLE
@@ -48,20 +55,45 @@ function profileColumns(gridData: LegendAIGridData): ColumnProfile[] {
48
55
 
49
56
  return gridData.columnDefs.map((col) => {
50
57
  const field = col.field ?? col.colId ?? '';
51
- const values = rows.map((r) => r[field]).filter(isNonNullable);
52
- const numericValues = values.filter(isNumber);
53
- const unique = new Set(values.map(String));
58
+ let nonNullCount = 0;
59
+ let stringCount = 0;
60
+ const numericValues: number[] = [];
61
+ const unique = new Set<string>();
62
+
63
+ for (const r of rows) {
64
+ const v = r[field];
65
+ if (v !== null && v !== undefined) {
66
+ nonNullCount++;
67
+ unique.add(String(v));
68
+ if (isNumber(v)) {
69
+ numericValues.push(v);
70
+ }
71
+ if (isString(v)) {
72
+ stringCount++;
73
+ }
74
+ }
75
+ }
76
+
54
77
  return {
55
78
  name: field,
56
- isNumeric: numericValues.length === values.length && values.length > 0,
57
- isString: values.length > 0 && values.every(isString),
79
+ isNumeric: numericValues.length === nonNullCount && nonNullCount > 0,
80
+ isString: nonNullCount > 0 && stringCount === nonNullCount,
58
81
  uniqueCount: unique.size,
59
- values,
82
+ nonNullCount,
60
83
  numericValues,
61
84
  };
62
85
  });
63
86
  }
64
87
 
88
+ function findBestCategoricalColumn(
89
+ profiles: ColumnProfile[],
90
+ rowCount: number,
91
+ ): ColumnProfile | undefined {
92
+ return profiles
93
+ .filter((c) => c.isString && c.uniqueCount > 1 && c.uniqueCount < rowCount)
94
+ .sort((a, b) => a.uniqueCount - b.uniqueCount)[0];
95
+ }
96
+
65
97
  function formatNumber(n: number): string {
66
98
  if (Number.isInteger(n) && Math.abs(n) < 1_000_000) {
67
99
  return n.toLocaleString();
@@ -111,12 +143,11 @@ function computeNumericMetrics(
111
143
  }
112
144
  }
113
145
 
114
- export function computeKeyMetrics(
115
- gridData: LegendAIGridData,
146
+ function computeKeyMetricsFromProfiles(
147
+ profiles: ColumnProfile[],
148
+ rowCount: number,
116
149
  ): LegendAIKeyMetric[] {
117
- const profiles = profileColumns(gridData);
118
150
  const metrics: LegendAIKeyMetric[] = [];
119
- const rowCount = gridData.rowData.length;
120
151
 
121
152
  metrics.push({
122
153
  label: 'Total Rows',
@@ -143,66 +174,122 @@ export function computeKeyMetrics(
143
174
  return metrics.slice(0, MAX_KEY_METRICS);
144
175
  }
145
176
 
146
- export function inferChartType(gridData: LegendAIGridData): LegendAIChartType {
147
- const profiles = profileColumns(gridData);
177
+ export function computeKeyMetrics(
178
+ gridData: LegendAIGridData,
179
+ ): LegendAIKeyMetric[] {
180
+ return computeKeyMetricsFromProfiles(
181
+ profileColumns(gridData),
182
+ gridData.rowData.length,
183
+ );
184
+ }
185
+
186
+ function inferChartTypeFromProfiles(
187
+ profiles: ColumnProfile[],
188
+ rowCount: number,
189
+ ): LegendAIChartType {
148
190
  const numericCols = profiles.filter((c) => c.isNumeric);
149
191
  const stringCols = profiles.filter((c) => c.isString && c.uniqueCount > 1);
150
192
 
151
193
  if (
152
194
  stringCols.length >= 1 &&
153
195
  numericCols.length >= 1 &&
154
- gridData.rowData.length <= MAX_BAR_CHART_ROWS
196
+ rowCount <= MAX_BAR_CHART_ROWS
155
197
  ) {
156
- if (gridData.rowData.length <= MAX_PIE_CHART_ROWS) {
198
+ if (rowCount <= MAX_PIE_CHART_ROWS) {
157
199
  return LegendAIChartType.PIE;
158
200
  }
159
201
  return LegendAIChartType.BAR;
160
202
  }
161
203
 
162
- if (numericCols.length >= 1 && gridData.rowData.length > 1) {
204
+ if (numericCols.length >= 1 && rowCount > 1) {
163
205
  return LegendAIChartType.BAR;
164
206
  }
165
207
 
208
+ if (rowCount > 1) {
209
+ const categoricalCol = findBestCategoricalColumn(profiles, rowCount);
210
+ if (categoricalCol) {
211
+ return categoricalCol.uniqueCount <= MAX_PIE_CHART_ROWS
212
+ ? LegendAIChartType.PIE
213
+ : LegendAIChartType.BAR;
214
+ }
215
+ }
216
+
166
217
  return LegendAIChartType.NONE;
167
218
  }
168
219
 
169
- export function computeChartData(
220
+ export function inferChartType(gridData: LegendAIGridData): LegendAIChartType {
221
+ return inferChartTypeFromProfiles(
222
+ profileColumns(gridData),
223
+ gridData.rowData.length,
224
+ );
225
+ }
226
+
227
+ function computeChartDataFromProfiles(
228
+ profiles: ColumnProfile[],
170
229
  gridData: LegendAIGridData,
171
230
  ): LegendAIChartDataPoint[] {
172
- const profiles = profileColumns(gridData);
173
231
  const numericCol = profiles.find((c) => c.isNumeric);
174
232
  const labelCol = profiles.find((c) => c.isString && c.uniqueCount > 1);
175
233
 
176
- if (!numericCol) {
234
+ if (numericCol) {
235
+ const field = numericCol.name;
236
+ const labelField = labelCol?.name;
237
+ const rows =
238
+ gridData.rowData.length > MAX_PROFILE_SAMPLE
239
+ ? gridData.rowData.slice(0, MAX_PROFILE_SAMPLE)
240
+ : gridData.rowData;
241
+
242
+ const entries = rows
243
+ .map((row) => {
244
+ const rawValue = row[field];
245
+ return {
246
+ label: labelField
247
+ ? String(row[labelField] ?? '')
248
+ : String(row[gridData.columnDefs[0]?.field ?? ''] ?? ''),
249
+ value: typeof rawValue === 'number' ? rawValue : 0,
250
+ };
251
+ })
252
+ .filter((e) => e.label.length > 0)
253
+ .sort((a, b) => b.value - a.value)
254
+ .slice(0, MAX_CHART_ITEMS);
255
+
256
+ return entries.map((e, i) => ({
257
+ label: e.label,
258
+ value: e.value,
259
+ colorIndex: i % CHART_PALETTE_COUNT,
260
+ }));
261
+ }
262
+
263
+ const categoricalCol = findBestCategoricalColumn(
264
+ profiles,
265
+ gridData.rowData.length,
266
+ );
267
+ if (!categoricalCol) {
177
268
  return [];
178
269
  }
179
270
 
180
- const field = numericCol.name;
181
- const labelField = labelCol?.name;
182
- const rows =
183
- gridData.rowData.length > MAX_PROFILE_SAMPLE
184
- ? gridData.rowData.slice(0, MAX_PROFILE_SAMPLE)
185
- : gridData.rowData;
271
+ const freqMap = new Map<string, number>();
272
+ for (const row of gridData.rowData) {
273
+ const val = String(row[categoricalCol.name] ?? '');
274
+ if (val.length > 0) {
275
+ freqMap.set(val, (freqMap.get(val) ?? 0) + 1);
276
+ }
277
+ }
186
278
 
187
- const entries = rows
188
- .map((row) => {
189
- const rawValue = row[field];
190
- return {
191
- label: labelField
192
- ? String(row[labelField] ?? '')
193
- : String(row[gridData.columnDefs[0]?.field ?? ''] ?? ''),
194
- value: typeof rawValue === 'number' ? rawValue : 0,
195
- };
196
- })
197
- .filter((e) => e.label.length > 0)
198
- .sort((a, b) => b.value - a.value)
199
- .slice(0, MAX_CHART_ITEMS);
200
-
201
- return entries.map((e, i) => ({
202
- label: e.label,
203
- value: e.value,
204
- colorIndex: i % CHART_PALETTE_COUNT,
205
- }));
279
+ return [...freqMap.entries()]
280
+ .sort((a, b) => b[1] - a[1])
281
+ .slice(0, MAX_CHART_ITEMS)
282
+ .map(([label, value], i) => ({
283
+ label,
284
+ value,
285
+ colorIndex: i % CHART_PALETTE_COUNT,
286
+ }));
287
+ }
288
+
289
+ export function computeChartData(
290
+ gridData: LegendAIGridData,
291
+ ): LegendAIChartDataPoint[] {
292
+ return computeChartDataFromProfiles(profileColumns(gridData), gridData);
206
293
  }
207
294
 
208
295
  export function computeTopItems(
@@ -211,10 +298,10 @@ export function computeTopItems(
211
298
  return computeChartData(gridData).slice(0, TOP_N_ITEMS);
212
299
  }
213
300
 
214
- export function findNumericColumnName(
301
+ function findNumericColumnNameFromProfiles(
302
+ profiles: ColumnProfile[],
215
303
  gridData: LegendAIGridData,
216
304
  ): string | undefined {
217
- const profiles = profileColumns(gridData);
218
305
  const numericCol = profiles.find((c) => c.isNumeric);
219
306
  if (!numericCol) {
220
307
  return undefined;
@@ -224,3 +311,26 @@ export function findNumericColumnName(
224
311
  );
225
312
  return colDef?.headerName ?? colDef?.field;
226
313
  }
314
+
315
+ export function findNumericColumnName(
316
+ gridData: LegendAIGridData,
317
+ ): string | undefined {
318
+ return findNumericColumnNameFromProfiles(profileColumns(gridData), gridData);
319
+ }
320
+
321
+ export function analyzeGridData(
322
+ gridData: LegendAIGridData,
323
+ ): LegendAIGridAnalysis {
324
+ const profiles = profileColumns(gridData);
325
+ const rowCount = gridData.rowData.length;
326
+ const chartType = inferChartTypeFromProfiles(profiles, rowCount);
327
+ return {
328
+ metrics: computeKeyMetricsFromProfiles(profiles, rowCount),
329
+ chartType,
330
+ chartData:
331
+ chartType === LegendAIChartType.NONE
332
+ ? []
333
+ : computeChartDataFromProfiles(profiles, gridData),
334
+ numericColumnName: findNumericColumnNameFromProfiles(profiles, gridData),
335
+ };
336
+ }