@finos/legend-lego 2.0.217 → 2.0.218

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 (50) hide show
  1. package/lib/index.css +2 -2
  2. package/lib/index.css.map +1 -1
  3. package/lib/legend-ai/LegendAIDocEnrichment.d.ts +14 -0
  4. package/lib/legend-ai/LegendAIDocEnrichment.d.ts.map +1 -1
  5. package/lib/legend-ai/LegendAIDocEnrichment.js +140 -17
  6. package/lib/legend-ai/LegendAIDocEnrichment.js.map +1 -1
  7. package/lib/legend-ai/LegendAITypes.d.ts +30 -1
  8. package/lib/legend-ai/LegendAITypes.d.ts.map +1 -1
  9. package/lib/legend-ai/LegendAITypes.js +31 -0
  10. package/lib/legend-ai/LegendAITypes.js.map +1 -1
  11. package/lib/legend-ai/LegendAI_LegendApplicationPlugin_Extension.d.ts +2 -2
  12. package/lib/legend-ai/LegendAI_LegendApplicationPlugin_Extension.d.ts.map +1 -1
  13. package/lib/legend-ai/LegendAI_LegendApplicationPlugin_Extension.js +1 -1
  14. package/lib/legend-ai/LegendAI_LegendApplicationPlugin_Extension.js.map +1 -1
  15. package/lib/legend-ai/components/LegendAIChat.d.ts.map +1 -1
  16. package/lib/legend-ai/components/LegendAIChat.js +62 -38
  17. package/lib/legend-ai/components/LegendAIChat.js.map +1 -1
  18. package/lib/legend-ai/components/LegendAIChatHelpers.d.ts +5 -0
  19. package/lib/legend-ai/components/LegendAIChatHelpers.d.ts.map +1 -1
  20. package/lib/legend-ai/components/LegendAIChatHelpers.js +8 -1
  21. package/lib/legend-ai/components/LegendAIChatHelpers.js.map +1 -1
  22. package/lib/legend-ai/index.d.ts +3 -2
  23. package/lib/legend-ai/index.d.ts.map +1 -1
  24. package/lib/legend-ai/index.js +3 -2
  25. package/lib/legend-ai/index.js.map +1 -1
  26. package/lib/legend-ai/stores/LegendAIChatProcessors.d.ts +27 -2
  27. package/lib/legend-ai/stores/LegendAIChatProcessors.d.ts.map +1 -1
  28. package/lib/legend-ai/stores/LegendAIChatProcessors.js +329 -86
  29. package/lib/legend-ai/stores/LegendAIChatProcessors.js.map +1 -1
  30. package/lib/legend-ai/stores/LegendAIJoinAnalysis.d.ts +11 -0
  31. package/lib/legend-ai/stores/LegendAIJoinAnalysis.d.ts.map +1 -1
  32. package/lib/legend-ai/stores/LegendAIJoinAnalysis.js +53 -19
  33. package/lib/legend-ai/stores/LegendAIJoinAnalysis.js.map +1 -1
  34. package/lib/legend-ai/stores/LegendAISqlHelpers.d.ts +4 -1
  35. package/lib/legend-ai/stores/LegendAISqlHelpers.d.ts.map +1 -1
  36. package/lib/legend-ai/stores/LegendAISqlHelpers.js +25 -2
  37. package/lib/legend-ai/stores/LegendAISqlHelpers.js.map +1 -1
  38. package/lib/legend-ai/stores/LegendAISqlJoinSanitizers.js +4 -4
  39. package/lib/legend-ai/stores/LegendAISqlJoinSanitizers.js.map +1 -1
  40. package/package.json +3 -3
  41. package/src/legend-ai/LegendAIDocEnrichment.ts +189 -23
  42. package/src/legend-ai/LegendAITypes.ts +54 -1
  43. package/src/legend-ai/LegendAI_LegendApplicationPlugin_Extension.ts +2 -0
  44. package/src/legend-ai/components/LegendAIChat.tsx +114 -78
  45. package/src/legend-ai/components/LegendAIChatHelpers.ts +9 -3
  46. package/src/legend-ai/index.ts +7 -0
  47. package/src/legend-ai/stores/LegendAIChatProcessors.ts +476 -105
  48. package/src/legend-ai/stores/LegendAIJoinAnalysis.ts +82 -19
  49. package/src/legend-ai/stores/LegendAISqlHelpers.ts +32 -2
  50. package/src/legend-ai/stores/LegendAISqlJoinSanitizers.ts +4 -4
@@ -15,11 +15,17 @@
15
15
  */
16
16
 
17
17
  import {
18
+ type AbstractPureGraphManager,
18
19
  type Multiplicity,
19
20
  extractElementNameFromPath,
20
21
  PRIMITIVE_TYPE,
22
+ GRAPH_MANAGER_EVENT,
21
23
  } from '@finos/legend-graph';
22
- import { isNonNullable } from '@finos/legend-shared';
24
+ import {
25
+ assertErrorThrown,
26
+ isNonNullable,
27
+ LogEvent,
28
+ } from '@finos/legend-shared';
23
29
  import {
24
30
  ClassDocumentationEntry,
25
31
  AssociationDocumentationEntry,
@@ -36,6 +42,7 @@ import type {
36
42
  LegendAIModelEntity,
37
43
  LegendAIModelAssociation,
38
44
  LegendAIModelProperty,
45
+ LegendAIFunctionInfo,
39
46
  } from './LegendAITypes.js';
40
47
  import type {
41
48
  LegendAIEnrichedBusinessContext,
@@ -247,6 +254,8 @@ function findDirectRelationship(
247
254
  return undefined;
248
255
  }
249
256
 
257
+ const MAX_SERVICE_RELATIONSHIPS = 25;
258
+
250
259
  /**
251
260
  * Infers cross-service relationships from association documentation entries.
252
261
  *
@@ -258,6 +267,9 @@ function findDirectRelationship(
258
267
  * - Which columns to JOIN on (shared column names)
259
268
  *
260
269
  * Used by both Data Space and Data Product AI integrations.
270
+ *
271
+ * Capped at {@link MAX_SERVICE_RELATIONSHIPS}: the pair space is quadratic in
272
+ * the service count, and every entry is rendered into the metadata prompt.
261
273
  */
262
274
  export function inferServiceRelationshipsFromAssociations(
263
275
  services: TDSServiceSchema[],
@@ -268,12 +280,10 @@ export function inferServiceRelationshipsFromAssociations(
268
280
  const relationships: LegendAIServiceRelationship[] = [];
269
281
  const seen = new Set<string>();
270
282
 
271
- for (let i = 0; i < services.length; i++) {
272
- for (let j = i + 1; j < services.length; j++) {
273
- const svcA = services[i];
274
- const svcB = services[j];
275
- if (!svcA || !svcB) {
276
- continue;
283
+ for (const [index, svcA] of services.entries()) {
284
+ for (const svcB of services.slice(index + 1)) {
285
+ if (relationships.length >= MAX_SERVICE_RELATIONSHIPS) {
286
+ return relationships;
277
287
  }
278
288
  const key = `${svcA.title}|${svcB.title}`;
279
289
  if (seen.has(key)) {
@@ -542,6 +552,28 @@ export function extractLambdaPreFilters(
542
552
  return results;
543
553
  }
544
554
 
555
+ /**
556
+ * Reads the filters already baked into a service query. Best effort: without
557
+ * them the service still works, the AI just misses its hardcoded constraints.
558
+ */
559
+ export async function extractServicePreFilters(
560
+ query: string,
561
+ graphManager: AbstractPureGraphManager,
562
+ ): Promise<TDSServicePreFilter[] | undefined> {
563
+ try {
564
+ const rawLambda = await graphManager.pureCodeToLambda(query);
565
+ const preFilters = extractLambdaPreFilters(rawLambda.body);
566
+ return preFilters.length > 0 ? preFilters : undefined;
567
+ } catch (error) {
568
+ assertErrorThrown(error);
569
+ graphManager.logService.debug(
570
+ LogEvent.create(GRAPH_MANAGER_EVENT.PARSING_FAILURE),
571
+ error,
572
+ );
573
+ return undefined;
574
+ }
575
+ }
576
+
545
577
  // ────────────────────────────────────────────────────────────────────────────
546
578
  // Model context extraction from DataSpace elementDocs
547
579
  // ────────────────────────────────────────────────────────────────────────────
@@ -577,6 +609,9 @@ function collectClassEntities(
577
609
  if (cls.docs.length > 0) {
578
610
  entity.description = cls.docs.join('; ');
579
611
  }
612
+ if (cls.milestoning !== undefined) {
613
+ entity.milestoning = cls.milestoning;
614
+ }
580
615
  return entity;
581
616
  });
582
617
  }
@@ -742,6 +777,13 @@ function buildRootEntityNlHints(
742
777
  category: 'queryable_hint',
743
778
  });
744
779
  }
780
+ if (rootEntityObj.milestoning) {
781
+ entries.push({
782
+ id: rootEntity,
783
+ description: `${rootEntityObj.name} is temporally versioned (${rootEntityObj.milestoning}) — include an as-of/business-date filter to select the intended snapshot`,
784
+ category: 'milestoning_hint',
785
+ });
786
+ }
745
787
  return entries;
746
788
  }
747
789
 
@@ -982,6 +1024,7 @@ function buildNlContextEntries(
982
1024
  entries.push(
983
1025
  ...buildAssociationNlHints(rootEntity, relatedEntities, modelContext),
984
1026
  ...buildExecutableNlHints(rootEntity, rootEntityObj, modelContext),
1027
+ ...buildFunctionNlHints(modelContext),
985
1028
  ...buildValidValueNlHints(
986
1029
  rootEntity,
987
1030
  relatedEntities,
@@ -1185,6 +1228,18 @@ export function buildSemanticPropertyIndex(
1185
1228
  // ────────────────────────────────────────────────────────────────────────────
1186
1229
 
1187
1230
  const MAX_RESOLVED_RELATED_ENTITIES = 5;
1231
+ const EXECUTABLE_COUNT_SCORE_CAP = 3;
1232
+
1233
+ // Counts executables whose root is each entity — a signal of "real query target".
1234
+ function buildExecutableCountByRoot(
1235
+ modelContext: LegendAIModelContext,
1236
+ ): Map<string, number> {
1237
+ const counts = new Map<string, number>();
1238
+ for (const exec of modelContext.executables ?? []) {
1239
+ counts.set(exec.rootEntityPath, (counts.get(exec.rootEntityPath) ?? 0) + 1);
1240
+ }
1241
+ return counts;
1242
+ }
1188
1243
 
1189
1244
  /**
1190
1245
  * Resolves root and related entities using keyword matching against the model
@@ -1199,6 +1254,7 @@ const MAX_RESOLVED_RELATED_ENTITIES = 5;
1199
1254
  * - description keyword match (+1)
1200
1255
  * - executable title/description match (+4)
1201
1256
  * - semantic property-doc match (+1 per token hit)
1257
+ * - executable count on the entity (+1 each, capped at 3)
1202
1258
  *
1203
1259
  * When queryable entities exist, only they are eligible as root.
1204
1260
  */
@@ -1274,12 +1330,17 @@ function scoreEntityByProperties(
1274
1330
  return score;
1275
1331
  }
1276
1332
 
1333
+ interface EntityScoreIndexes {
1334
+ semanticIndex: Map<string, Set<string>>;
1335
+ executableByRoot: Map<string, string[]>;
1336
+ executableCountByRoot: Map<string, number>;
1337
+ enumValueIndex: Map<string, Set<string>>;
1338
+ }
1339
+
1277
1340
  function scoreEntity(
1278
1341
  entity: LegendAIModelEntity,
1279
1342
  tokens: string[],
1280
- semanticIndex: Map<string, Set<string>>,
1281
- executableByRoot: Map<string, string[]>,
1282
- enumValueIndex: Map<string, Set<string>>,
1343
+ indexes: EntityScoreIndexes,
1283
1344
  ): number {
1284
1345
  const nameLower = entity.name.toLowerCase();
1285
1346
  let score =
@@ -1295,15 +1356,22 @@ function scoreEntity(
1295
1356
  const descLower = entity.description.toLowerCase();
1296
1357
  score += tokens.filter((t) => descLower.includes(t)).length;
1297
1358
  }
1298
- const execWords = executableByRoot.get(entity.path);
1359
+ const execWords = indexes.executableByRoot.get(entity.path);
1299
1360
  if (execWords) {
1300
1361
  score +=
1301
1362
  tokens.filter((t) => execWords.some((w) => w === t || w.includes(t)))
1302
1363
  .length * 4;
1303
1364
  }
1304
- score += tokens.filter((t) => semanticIndex.get(t)?.has(entity.path)).length;
1365
+ score += Math.min(
1366
+ indexes.executableCountByRoot.get(entity.path) ?? 0,
1367
+ EXECUTABLE_COUNT_SCORE_CAP,
1368
+ );
1369
+ score += tokens.filter((t) =>
1370
+ indexes.semanticIndex.get(t)?.has(entity.path),
1371
+ ).length;
1305
1372
  score +=
1306
- tokens.filter((t) => enumValueIndex.get(t)?.has(entity.path)).length * 2;
1373
+ tokens.filter((t) => indexes.enumValueIndex.get(t)?.has(entity.path))
1374
+ .length * 2;
1307
1375
  return score;
1308
1376
  }
1309
1377
 
@@ -1311,20 +1379,17 @@ export function rankEntities(
1311
1379
  question: string,
1312
1380
  modelContext: LegendAIModelContext,
1313
1381
  ): { entity: LegendAIModelEntity; score: number }[] {
1314
- const executableByRoot = buildExecutableByRootIndex(modelContext);
1315
- const semanticIndex = buildSemanticPropertyIndex(modelContext);
1316
- const enumValueIndex = buildEnumValueIndex(modelContext);
1382
+ const indexes: EntityScoreIndexes = {
1383
+ semanticIndex: buildSemanticPropertyIndex(modelContext),
1384
+ executableByRoot: buildExecutableByRootIndex(modelContext),
1385
+ executableCountByRoot: buildExecutableCountByRoot(modelContext),
1386
+ enumValueIndex: buildEnumValueIndex(modelContext),
1387
+ };
1317
1388
  const tokens = tokenizeText(question);
1318
1389
  return modelContext.entities
1319
1390
  .map((entity) => ({
1320
1391
  entity,
1321
- score: scoreEntity(
1322
- entity,
1323
- tokens,
1324
- semanticIndex,
1325
- executableByRoot,
1326
- enumValueIndex,
1327
- ),
1392
+ score: scoreEntity(entity, tokens, indexes),
1328
1393
  }))
1329
1394
  .sort((a, b) => b.score - a.score);
1330
1395
  }
@@ -1490,6 +1555,34 @@ export function extractFilteredColumns(pureQuery: string): string[] {
1490
1555
 
1491
1556
  const MAX_PROBED_VALUES_PER_COLUMN = 30;
1492
1557
 
1558
+ const MAX_PRIOR_SQL_HINT_CHARS = 2000;
1559
+
1560
+ /**
1561
+ * Builds orchestrator hints describing a prior SQL attempt that failed or
1562
+ * returned no rows, so the Pure query is generated with that context in mind.
1563
+ */
1564
+ export function buildPriorSqlFailureHints(
1565
+ failedSql: string | undefined,
1566
+ failedReason: string | undefined,
1567
+ ): LegendAIAdditionalNlModelContext[] {
1568
+ const entries: LegendAIAdditionalNlModelContext[] = [];
1569
+ if (failedReason !== undefined && failedReason.length > 0) {
1570
+ entries.push({
1571
+ id: 'prior_sql_failure_reason',
1572
+ description: `A prior SQL attempt for this question did not yield a usable answer: ${failedReason.slice(0, MAX_PRIOR_SQL_HINT_CHARS)}. Generate a query using the model's entities, relationships, and functions rather than repeating that approach.`,
1573
+ category: 'prior_sql_failure',
1574
+ });
1575
+ }
1576
+ if (failedSql !== undefined && failedSql.length > 0) {
1577
+ entries.push({
1578
+ id: 'prior_sql_failure_query',
1579
+ description: `The prior SQL that failed or returned 0 rows was: ${failedSql.slice(0, MAX_PRIOR_SQL_HINT_CHARS)}. Avoid the filters or joins that produced no rows.`,
1580
+ category: 'prior_sql_failure',
1581
+ });
1582
+ }
1583
+ return entries;
1584
+ }
1585
+
1493
1586
  export function buildProbedValueHints(
1494
1587
  probed: Map<string, string[]>,
1495
1588
  ): LegendAIAdditionalNlModelContext[] {
@@ -1689,6 +1782,75 @@ function buildExecutableSection(
1689
1782
  return lines.join('\n');
1690
1783
  }
1691
1784
 
1785
+ const MAX_MILESTONING_ENTITIES = 15;
1786
+
1787
+ // Lists temporally-versioned entities so the LLM adds as-of/business-date filters.
1788
+ function buildMilestoningSection(
1789
+ modelContext: LegendAIModelContext,
1790
+ ): string | undefined {
1791
+ const temporal = modelContext.entities.filter((e) => e.milestoning);
1792
+ if (temporal.length === 0) {
1793
+ return undefined;
1794
+ }
1795
+ const lines = [
1796
+ '## Temporal Entities (time-versioned)',
1797
+ 'These entities track changes over time — include an as-of/business-date filter to select the intended snapshot:',
1798
+ ];
1799
+ for (const entity of temporal.slice(0, MAX_MILESTONING_ENTITIES)) {
1800
+ lines.push(`- ${entity.name} [${entity.milestoning}]`);
1801
+ }
1802
+ if (temporal.length > MAX_MILESTONING_ENTITIES) {
1803
+ lines.push(
1804
+ `\n(${temporal.length - MAX_MILESTONING_ENTITIES} additional temporal entities omitted)`,
1805
+ );
1806
+ }
1807
+ return lines.join('\n');
1808
+ }
1809
+
1810
+ const MAX_ENRICHMENT_FUNCTIONS = 20;
1811
+
1812
+ function formatFunctionSignature(fn: LegendAIFunctionInfo): string {
1813
+ const params = fn.parameters.map((p) => `${p.name}: ${p.type}`).join(', ');
1814
+ const returns = fn.returnType ? `: ${fn.returnType}` : '';
1815
+ return `${fn.name}(${params})${returns}`;
1816
+ }
1817
+
1818
+ // Lists the dataspace's callable model functions so the orchestrator can use them.
1819
+ function buildFunctionSection(
1820
+ modelContext: LegendAIModelContext,
1821
+ ): string | undefined {
1822
+ if (!modelContext.functions?.length) {
1823
+ return undefined;
1824
+ }
1825
+ const lines = [
1826
+ '## Available Functions',
1827
+ 'These model functions can be called in queries:',
1828
+ ];
1829
+ for (const fn of modelContext.functions.slice(0, MAX_ENRICHMENT_FUNCTIONS)) {
1830
+ lines.push(`- ${formatFunctionSignature(fn)}`);
1831
+ }
1832
+ if (modelContext.functions.length > MAX_ENRICHMENT_FUNCTIONS) {
1833
+ lines.push(
1834
+ `\n(${modelContext.functions.length - MAX_ENRICHMENT_FUNCTIONS} additional functions omitted)`,
1835
+ );
1836
+ }
1837
+ return lines.join('\n');
1838
+ }
1839
+
1840
+ function buildFunctionNlHints(
1841
+ modelContext: LegendAIModelContext,
1842
+ ): LegendAIAdditionalNlModelContext[] {
1843
+ return (modelContext.functions ?? [])
1844
+ .slice(0, MAX_ENRICHMENT_FUNCTIONS)
1845
+ .map((fn) => {
1846
+ return {
1847
+ id: fn.functionPath,
1848
+ description: `Callable function: ${formatFunctionSignature(fn)}`,
1849
+ category: 'function',
1850
+ };
1851
+ });
1852
+ }
1853
+
1692
1854
  function buildPropToEnumPathMap(
1693
1855
  modelContext: LegendAIModelContext,
1694
1856
  enumPathMap: Map<string, string[]>,
@@ -1934,6 +2096,8 @@ export function buildModelContextEnrichmentText(
1934
2096
  buildEnumerationSection(modelContext),
1935
2097
  buildAssociationSection(modelContext),
1936
2098
  buildExecutableSection(modelContext),
2099
+ buildFunctionSection(modelContext),
2100
+ buildMilestoningSection(modelContext),
1937
2101
  buildColumnEnumMappingSection(modelContext, services),
1938
2102
  buildColumnDescriptionSection(modelContext, services),
1939
2103
  buildServiceJoinSection(modelContext, services),
@@ -2094,6 +2258,8 @@ export function buildModelCatalogText(
2094
2258
  buildCatalogRelationshipsSection(modelContext),
2095
2259
  buildCatalogEnumerationsSection(modelContext),
2096
2260
  buildCatalogExecutablesSection(modelContext),
2261
+ buildFunctionSection(modelContext),
2262
+ buildMilestoningSection(modelContext),
2097
2263
  ].filter(isNonNullable);
2098
2264
 
2099
2265
  if (sections.length === 0) {
@@ -37,6 +37,7 @@ export class TDSColumnSchema {
37
37
  type?: string;
38
38
  documentation?: string;
39
39
  sampleValues?: string;
40
+ sampleValuesComplete?: boolean;
40
41
  /** Whether this column is nullable (multiplicity lowerBound === 0). */
41
42
  nullable?: boolean;
42
43
  /** Physical relational type (e.g. 'VARCHAR(100)', 'DECIMAL(18,4)'). */
@@ -156,6 +157,34 @@ export const COVERAGE_NAME_SANDBOX = 'Legend-AI-Sandbox';
156
157
  */
157
158
  export const TDS_SAMPLE_VALUES_DELIMITER = '-- e.g.';
158
159
 
160
+ /**
161
+ * Splits a TDS column `doc` on the sample-values delimiter. Without the
162
+ * delimiter the whole string is the documentation; blank halves are omitted.
163
+ */
164
+ export function parseTDSColumnDoc(doc: string): {
165
+ documentation?: string;
166
+ sampleValues?: string;
167
+ } {
168
+ const delimiterIndex = doc.indexOf(TDS_SAMPLE_VALUES_DELIMITER);
169
+ const result: { documentation?: string; sampleValues?: string } = {};
170
+ const documentation = (
171
+ delimiterIndex === -1 ? doc : doc.substring(0, delimiterIndex)
172
+ ).trim();
173
+ if (documentation) {
174
+ result.documentation = documentation;
175
+ }
176
+ if (delimiterIndex === -1) {
177
+ return result;
178
+ }
179
+ const sampleValues = doc
180
+ .substring(delimiterIndex + TDS_SAMPLE_VALUES_DELIMITER.length)
181
+ .trim();
182
+ if (sampleValues) {
183
+ result.sampleValues = sampleValues;
184
+ }
185
+ return result;
186
+ }
187
+
159
188
  export function getTodayISO(): string {
160
189
  return new Date().toISOString().slice(0, 10);
161
190
  }
@@ -165,6 +194,9 @@ export const LEGEND_AI_ORCHESTRATOR_FALLBACK_ACTION_ID =
165
194
 
166
195
  export const LEGEND_AI_ALTERNATE_ROOT_ACTION_ID = 'alternate-root';
167
196
 
197
+ /** Prefix of a Pure data-product accessor query — `#P{path}#->...`. */
198
+ export const DATA_PRODUCT_ACCESSOR_PREFIX = '#P{';
199
+
168
200
  export const LEGEND_AI_FEEDBACK_PROMPT = 'Was this helpful?';
169
201
  export const LEGEND_AI_DEFAULT_MODEL_LABEL = 'Auto';
170
202
 
@@ -204,6 +236,12 @@ export class LegendAIThinkingStep {
204
236
  status!: LegendAIThinkingStepStatus;
205
237
  }
206
238
 
239
+ export enum LegendAIPythonCodeStatus {
240
+ LOADING = 'loading',
241
+ READY = 'ready',
242
+ ERROR = 'error',
243
+ }
244
+
207
245
  export enum LegendAIErrorType {
208
246
  PERMISSION = 'PERMISSION',
209
247
  NETWORK = 'NETWORK',
@@ -249,7 +287,13 @@ export class LegendAIUserMessage {
249
287
  text!: string;
250
288
  }
251
289
 
252
- export interface LegendAIFallbackAction {
290
+ /** A prior SQL attempt carried into orchestrator (Pure) query generation. */
291
+ export interface LegendAIPriorSqlFailure {
292
+ failedSql?: string;
293
+ failedReason?: string;
294
+ }
295
+
296
+ export interface LegendAIFallbackAction extends LegendAIPriorSqlFailure {
253
297
  label: string;
254
298
  actionId: string;
255
299
  resolvedEntities?: LegendAIResolvedEntities;
@@ -575,6 +619,7 @@ export interface LegendAIModelEntity {
575
619
  properties: LegendAIModelProperty[];
576
620
  isRootMapped?: boolean;
577
621
  isQueryable?: boolean;
622
+ milestoning?: string;
578
623
  }
579
624
 
580
625
  export interface LegendAIModelAssociation {
@@ -618,12 +663,20 @@ export interface LegendAIPhysicalDataset {
618
663
  table?: string;
619
664
  }
620
665
 
666
+ export interface LegendAIFunctionInfo {
667
+ name: string;
668
+ functionPath: string;
669
+ returnType?: string;
670
+ parameters: LegendAIParameterInfo[];
671
+ }
672
+
621
673
  export interface LegendAIModelContext {
622
674
  entities: LegendAIModelEntity[];
623
675
  associations: LegendAIModelAssociation[];
624
676
  enumerations?: LegendAIEnumerationInfo[];
625
677
  executables?: LegendAIExecutableInfo[];
626
678
  datasets?: LegendAIPhysicalDataset[];
679
+ functions?: LegendAIFunctionInfo[];
627
680
  dataspaceDescription?: string;
628
681
  }
629
682
 
@@ -403,6 +403,7 @@ export abstract class LegendAI_LegendApplicationPlugin_Extension extends LegendA
403
403
  metadata: LegendAIProductMetadata,
404
404
  config: LegendAIConfig,
405
405
  diagnosis?: string,
406
+ services?: TDSServiceSchema[],
406
407
  ): Promise<LegendAIResultAnalysis | undefined>;
407
408
 
408
409
  /**
@@ -506,6 +507,7 @@ export abstract class LegendAI_LegendApplicationPlugin_Extension extends LegendA
506
507
  _accessPoints: TDSServiceSchema[],
507
508
  _dataProductCoordinates: LegendAIOrchestratorDataProductCoordinates,
508
509
  _config: LegendAIConfig,
510
+ _question?: string,
509
511
  ): Promise<void> {
510
512
  return Promise.resolve();
511
513
  }