@finos/legend-lego 2.0.188 → 2.0.190

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 (44) hide show
  1. package/lib/index.css +2 -2
  2. package/lib/index.css.map +1 -1
  3. package/lib/legend-ai/LegendAITypes.d.ts +196 -0
  4. package/lib/legend-ai/LegendAITypes.d.ts.map +1 -0
  5. package/lib/legend-ai/LegendAITypes.js +281 -0
  6. package/lib/legend-ai/LegendAITypes.js.map +1 -0
  7. package/lib/legend-ai/LegendAI_LegendApplicationPlugin_Extension.d.ts +127 -0
  8. package/lib/legend-ai/LegendAI_LegendApplicationPlugin_Extension.d.ts.map +1 -0
  9. package/lib/legend-ai/LegendAI_LegendApplicationPlugin_Extension.js +63 -0
  10. package/lib/legend-ai/LegendAI_LegendApplicationPlugin_Extension.js.map +1 -0
  11. package/lib/legend-ai/__test-utils__/LegendAITestUtils.d.ts +29 -0
  12. package/lib/legend-ai/__test-utils__/LegendAITestUtils.d.ts.map +1 -0
  13. package/lib/legend-ai/__test-utils__/LegendAITestUtils.js +98 -0
  14. package/lib/legend-ai/__test-utils__/LegendAITestUtils.js.map +1 -0
  15. package/lib/legend-ai/components/LegendAIChat.d.ts +23 -0
  16. package/lib/legend-ai/components/LegendAIChat.d.ts.map +1 -0
  17. package/lib/legend-ai/components/LegendAIChat.js +179 -0
  18. package/lib/legend-ai/components/LegendAIChat.js.map +1 -0
  19. package/lib/legend-ai/components/LegendAIErrorBoundary.d.ts +31 -0
  20. package/lib/legend-ai/components/LegendAIErrorBoundary.d.ts.map +1 -0
  21. package/lib/legend-ai/components/LegendAIErrorBoundary.js +35 -0
  22. package/lib/legend-ai/components/LegendAIErrorBoundary.js.map +1 -0
  23. package/lib/legend-ai/components/LegendAIResultGrid.d.ts +20 -0
  24. package/lib/legend-ai/components/LegendAIResultGrid.d.ts.map +1 -0
  25. package/lib/legend-ai/components/LegendAIResultGrid.js +90 -0
  26. package/lib/legend-ai/components/LegendAIResultGrid.js.map +1 -0
  27. package/lib/legend-ai/index.d.ts +22 -0
  28. package/lib/legend-ai/index.d.ts.map +1 -0
  29. package/lib/legend-ai/index.js +22 -0
  30. package/lib/legend-ai/index.js.map +1 -0
  31. package/lib/legend-ai/stores/LegendAIChatState.d.ts +46 -0
  32. package/lib/legend-ai/stores/LegendAIChatState.d.ts.map +1 -0
  33. package/lib/legend-ai/stores/LegendAIChatState.js +559 -0
  34. package/lib/legend-ai/stores/LegendAIChatState.js.map +1 -0
  35. package/package.json +7 -3
  36. package/src/legend-ai/LegendAITypes.ts +386 -0
  37. package/src/legend-ai/LegendAI_LegendApplicationPlugin_Extension.ts +208 -0
  38. package/src/legend-ai/__test-utils__/LegendAITestUtils.ts +139 -0
  39. package/src/legend-ai/components/LegendAIChat.tsx +502 -0
  40. package/src/legend-ai/components/LegendAIErrorBoundary.tsx +42 -0
  41. package/src/legend-ai/components/LegendAIResultGrid.tsx +132 -0
  42. package/src/legend-ai/index.ts +46 -0
  43. package/src/legend-ai/stores/LegendAIChatState.ts +1004 -0
  44. package/tsconfig.json +8 -0
@@ -0,0 +1,386 @@
1
+ /**
2
+ * Copyright (c) 2026-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import type { DataGridColumnDefinition } from '../data-grid/index.js';
18
+ import type {
19
+ TDSRowDataType,
20
+ QueryExplicitExecutionContextInfo,
21
+ } from '@finos/legend-graph';
22
+ import type { LegendApplicationPlugin } from '@finos/legend-application';
23
+ import {
24
+ LegendAI_LegendApplicationPlugin_Extension,
25
+ type LegendAIOrchestratorDataProductCoordinates,
26
+ } from './LegendAI_LegendApplicationPlugin_Extension.js';
27
+
28
+ export class TDSColumnSchema {
29
+ name!: string;
30
+ type?: string;
31
+ documentation?: string;
32
+ sampleValues?: string;
33
+ }
34
+
35
+ export enum TDSServiceSourceType {
36
+ SERVICE = 'service',
37
+ ACCESS_POINT = 'accessPoint',
38
+ }
39
+
40
+ export class TDSServiceSchema {
41
+ title!: string;
42
+ description?: string;
43
+ pattern!: string;
44
+ columns!: TDSColumnSchema[];
45
+ parameters!: string[];
46
+ /**
47
+ * Indicates the source of this service schema.
48
+ * - SERVICE: traditional DataSpace service executable (uses `FROM service(...)` SQL syntax)
49
+ * - ACCESS_POINT: data product access point (uses `FROM p(...)` SQL syntax with lakehouse runtime)
50
+ */
51
+ sourceType?: TDSServiceSourceType;
52
+ /** Full data product path (e.g. 'my::package::DataProduct'), used with `p()` syntax for access points. */
53
+ dataProductPath?: string;
54
+ }
55
+
56
+ export class LegendAIConfig {
57
+ enabled!: boolean;
58
+ llmServiceUrl!: string | undefined;
59
+ llmModelName!: string | undefined;
60
+ sqlExecutionUrl!: string | undefined;
61
+ orchestratorUrl!: string | undefined;
62
+ marketplaceSearchUrl!: string | undefined;
63
+ engineUrl!: string | undefined;
64
+ orchestratorAuthToken?: string;
65
+ maxJudgeAttempts?: number;
66
+ /** Lakehouse runtime environment name (e.g. 'prod', 'uat'). Defaults to 'prod' when not set. */
67
+ lakehouseEnvironment?: string;
68
+ }
69
+
70
+ export const DEFAULT_LEGEND_AI_CONFIG: LegendAIConfig = Object.freeze({
71
+ enabled: false,
72
+ llmServiceUrl: undefined,
73
+ llmModelName: undefined,
74
+ sqlExecutionUrl: undefined,
75
+ orchestratorUrl: undefined,
76
+ marketplaceSearchUrl: undefined,
77
+ engineUrl: undefined,
78
+ });
79
+
80
+ export function findLegendAIPlugin(
81
+ plugins: LegendApplicationPlugin[],
82
+ ): LegendAI_LegendApplicationPlugin_Extension | undefined {
83
+ return plugins.find(
84
+ (p): p is LegendAI_LegendApplicationPlugin_Extension =>
85
+ p instanceof LegendAI_LegendApplicationPlugin_Extension,
86
+ );
87
+ }
88
+
89
+ export class LegendAIGridData {
90
+ columnDefs!: DataGridColumnDefinition[];
91
+ rowData!: TDSRowDataType[];
92
+ }
93
+
94
+ export function buildColumnDefsFromNames(
95
+ columns: string[],
96
+ ): DataGridColumnDefinition[] {
97
+ return columns.map((col) => ({
98
+ colId: col,
99
+ headerName: col,
100
+ field: col,
101
+ }));
102
+ }
103
+
104
+ export enum LegendAIThinkingStepStatus {
105
+ ACTIVE = 'active',
106
+ DONE = 'done',
107
+ ERROR = 'error',
108
+ }
109
+
110
+ export class LegendAIThinkingStep {
111
+ label!: string;
112
+ status!: LegendAIThinkingStepStatus;
113
+ }
114
+
115
+ export enum LegendAIMessageRole {
116
+ USER = 'user',
117
+ ASSISTANT = 'assistant',
118
+ }
119
+
120
+ export class LegendAIUserMessage {
121
+ id!: string;
122
+ role!: LegendAIMessageRole.USER;
123
+ text!: string;
124
+ }
125
+
126
+ export class LegendAIAssistantMessage {
127
+ id!: string;
128
+ role!: LegendAIMessageRole.ASSISTANT;
129
+ thinkingSteps!: LegendAIThinkingStep[];
130
+ sql!: string | null;
131
+ textAnswer!: string | null;
132
+ gridData!: LegendAIGridData | null;
133
+ error!: string | null;
134
+ sqlGenTime!: string | null;
135
+ execTime!: string | null;
136
+ thinkingDuration!: string | null;
137
+ isProcessing!: boolean;
138
+ isExecuting!: boolean;
139
+ suggestedQueries!: string[];
140
+ }
141
+
142
+ export type LegendAIMessage = LegendAIUserMessage | LegendAIAssistantMessage;
143
+
144
+ export class LegendAIConversationTurn {
145
+ question!: string;
146
+ sql!: string;
147
+ /**
148
+ * The intent classification of this turn, allowing downstream prompts
149
+ * to differentiate prior metadata answers from SQL query results.
150
+ */
151
+ intent?: LegendAIQuestionIntent;
152
+ }
153
+
154
+ export interface LegendAIChatState {
155
+ questionText: string;
156
+ setQuestionText: (text: string) => void;
157
+ isSending: boolean;
158
+ messages: LegendAIMessage[];
159
+ askQuestion: () => void;
160
+ askQuestionWithIntent: (text: string, intent: LegendAIQuestionIntent) => void;
161
+ clearChat: () => void;
162
+ expandedThinking: Set<number>;
163
+ toggleThinking: (index: number) => void;
164
+ conversationRef: { readonly current: HTMLDivElement | null };
165
+ }
166
+
167
+ export interface LegendAIServiceSummary {
168
+ title: string;
169
+ description?: string;
170
+ }
171
+
172
+ export interface LegendAIAccessPointInfo {
173
+ title?: string;
174
+ description?: string;
175
+ }
176
+
177
+ export interface LegendAITagInfo {
178
+ profile: string;
179
+ value: string;
180
+ }
181
+
182
+ export class LegendAIAccessPointGroupInfo {
183
+ title!: string;
184
+ description?: string;
185
+ accessPoints!: LegendAIAccessPointInfo[];
186
+ }
187
+
188
+ export class LegendAIProductMetadata {
189
+ name!: string;
190
+ description?: string;
191
+ coordinates!: string;
192
+ serviceSummaries!: LegendAIServiceSummary[];
193
+ accessPointGroups?: LegendAIAccessPointGroupInfo[];
194
+ tags?: LegendAITagInfo[];
195
+ supportInfo?: string;
196
+ }
197
+
198
+ export enum LegendAIQuestionIntent {
199
+ DATA_QUERY = 'data_query',
200
+ METADATA = 'metadata',
201
+ ORCHESTRATOR = 'orchestrator',
202
+ }
203
+
204
+ export const METADATA_SIGNAL_PATTERNS: readonly RegExp[] = Object.freeze([
205
+ /\b(?:what\s+does\s+this\s+(?:data\s*product|dataspace|data\s*space|product)\s+(?:do|provide|contain|offer|have))\b/,
206
+ /\b(?:describe|explain|summarize|summary|overview)\s+(?:this\s+)?(?:data\s*product|dataspace|data\s*space|product)\b/,
207
+ /\b(?:tell\s+me\s+about)\s+(?:this\s+)?(?:data\s*product|dataspace|data\s*space|product)\b/,
208
+ /\b(?:what\s+(?:services?|endpoints?|access\s*points?|columns?|fields?)\s+(?:are|is|does))\b/,
209
+ /\b(?:list|what\s+are)\s+(?:the\s+)?(?:available\s+)?(?:services?|endpoints?|capabilities)\b/,
210
+ /\b(?:list|what\s+are)\s+(?:the\s+)?(?:available\s+)?access\s*points?\b/,
211
+ /\b(?:who\s+(?:owns?|maintains?|created?|manages?|supports?))\b/,
212
+ /\b(?:owner(?:ship)?|maintainer|contact\s+(?:info|email|team|details)|support\s+(?:info|email|team))\b/,
213
+ /\b(?:classifications?|tags?|stereotypes?)\s+(?:of|for|on)\s+(?:this|the)\b/,
214
+ /\bsummarize\s+what\s+(?:you|this|it)\s+(?:have|has|provide|offers?|contains?)\b/,
215
+ /\bwhat\s+(?:do\s+you|does\s+it|does\s+this)\s+(?:have|provide|offer|contain)\b/,
216
+ /\b(?:how\s+many)\s+(?:services?|endpoints?|access\s*points?|columns?|fields?)\b/,
217
+ /\b(?:what\s+(?:type|kind|format))\s+(?:of|is)\b/,
218
+ /^(?:summarize|describe|explain|overview)\b/,
219
+ /\b(?:summary|overview|description)\s+(?:of|about)\b/,
220
+ /\bwhat\s+(?:is|are)\s+(?:this|the|it)\b/,
221
+ /\btell\s+me\s+(?:about|more)\b/,
222
+ /\bwhat\s+(?:\S+\s+)*(?:provides?|offers?|contains?|includes?)\s*$/,
223
+ /\bhow\s+does\s+(?:\S+\s+)*(?:work|function|operate)\b/,
224
+ /\bwhat\s+can\s+(?:i|we|you)\b/,
225
+ /\b(?:used\s+for|meant\s+for|designed\s+for|purpose\s+of)\b/,
226
+ /\b(?:help\s+me\s+(?:understand|with))\b/,
227
+ /\bwhat\s+(?:information|data|content|datasets?)\s+(?:is|are|does)\b/,
228
+ /\b(?:what\s+does)\s+\S+(?:\s+\S+)*\s+(?:do|provide|offer|contain|include|cover)\b/,
229
+ ]);
230
+
231
+ export const DATA_QUERY_SIGNAL_PATTERNS: readonly RegExp[] = Object.freeze([
232
+ /\b(?:select|query|sql|rows?|records?|count|sum|avg|average|min|max|total)\b/,
233
+ /\b(?:top\s+\d+|first\s+\d+|last\s+\d+|limit\s+\d+)\b/,
234
+ /\b(?:filter|where|group\s+by|order\s+by|sort|join|aggregate)\b/,
235
+ /\b(?:distinct|unique)\s+(?:values?|entries?|items?)/,
236
+ /\bfrom\s+(?:\S+\s+)*service\b/,
237
+ /\b(?:show|give|get|fetch|retrieve|pull|find|provide|display|return)\s+(?:me\s+)?/,
238
+ /\b(?:compare|comparison|versus|vs\.?)\b/,
239
+ /\b(?:what\s+percentage|what\s+%|what\s+proportion|what\s+fraction)\b/,
240
+ /\b(?:which\s+\w+s?\s+(?:does|do|did|has|have|is|are|generate|earn|produce))\b/,
241
+ /\b(?:how\s+(?:much|many|often))\b/,
242
+ /\b\d{4}[-/]\d{2}[-/]\d{2}\b/,
243
+ /\b(?:as\s+of|since|before|after|between|from|until|latest|recent|yesterday|today)\b/,
244
+ /\blast\s+(?:year|month|week|quarter|fiscal)\b/,
245
+ /\b(?:fiscal\s+(?:year|quarter)|fy\s*\d|q[1-4]\s*\d{4})\b/,
246
+ /\b(?:sedol|isin|cusip|ticker|symbol)[\s:=]*\w+/,
247
+ /\b(?:revenue|sales|income|profit|earnings|margin|growth|volume|price|rate|exposure)\b/,
248
+ /\b(?:breakdown|distribution|composition|split|allocation|attribution|ranking|trend)\b/,
249
+ /\b(?:per\s+(?:country|region|year|month|quarter|segment|category))\b/,
250
+ /\b(?:grouped?\s+by|broken?\s+down\s+by|split\s+by|segmented?\s+by)\b/,
251
+ ]);
252
+
253
+ const PRODUCT_REFERENCE_PATTERN =
254
+ /\b(?:this|the)\s+(?:data\s*product|dataspace|data\s*space|product)\b/;
255
+
256
+ const STRUCTURAL_KEYWORD_PATTERN =
257
+ /\b(?:services?|endpoints?|access\s*points?|capabilities|owner|maintainer|support)\b/;
258
+
259
+ function countPatternMatches(
260
+ question: string,
261
+ patterns: readonly RegExp[],
262
+ ): number {
263
+ let count = 0;
264
+ for (const pattern of patterns) {
265
+ if (pattern.test(question)) {
266
+ count++;
267
+ }
268
+ }
269
+ return count;
270
+ }
271
+
272
+ /**
273
+ * Result of the fast deterministic regex-based classifier.
274
+ * Includes the scores and resolved intent so callers can decide
275
+ * whether the classification is confident or needs LLM arbitration.
276
+ */
277
+ export class QuestionIntentClassification {
278
+ intent!: LegendAIQuestionIntent;
279
+ metaScore!: number;
280
+ dataScore!: number;
281
+ ambiguous!: boolean;
282
+ }
283
+
284
+ /**
285
+ * Fast deterministic regex classifier (sync, < 1ms).
286
+ * Returns both the resolved intent AND whether the result is ambiguous,
287
+ * so callers can decide to escalate to an LLM for ambiguous cases.
288
+ */
289
+ export function classifyQuestionIntentFast(
290
+ question: string,
291
+ hasServices: boolean,
292
+ ): QuestionIntentClassification {
293
+ const q = question.toLowerCase().trim();
294
+
295
+ const metaScore = countPatternMatches(q, METADATA_SIGNAL_PATTERNS);
296
+ const dataScore = countPatternMatches(q, DATA_QUERY_SIGNAL_PATTERNS);
297
+
298
+ if (metaScore > 0 && dataScore === 0) {
299
+ return {
300
+ intent: LegendAIQuestionIntent.METADATA,
301
+ metaScore,
302
+ dataScore,
303
+ ambiguous: false,
304
+ };
305
+ }
306
+ if (dataScore > 0 && metaScore === 0) {
307
+ return {
308
+ intent: LegendAIQuestionIntent.DATA_QUERY,
309
+ metaScore,
310
+ dataScore,
311
+ ambiguous: false,
312
+ };
313
+ }
314
+
315
+ if (metaScore > 0 && dataScore > 0) {
316
+ if (dataScore >= metaScore * 2) {
317
+ return {
318
+ intent: LegendAIQuestionIntent.DATA_QUERY,
319
+ metaScore,
320
+ dataScore,
321
+ ambiguous: false,
322
+ };
323
+ }
324
+ if (
325
+ PRODUCT_REFERENCE_PATTERN.test(q) ||
326
+ STRUCTURAL_KEYWORD_PATTERN.test(q)
327
+ ) {
328
+ return {
329
+ intent: LegendAIQuestionIntent.METADATA,
330
+ metaScore,
331
+ dataScore,
332
+ ambiguous: false,
333
+ };
334
+ }
335
+ const tentative =
336
+ metaScore > dataScore
337
+ ? LegendAIQuestionIntent.METADATA
338
+ : LegendAIQuestionIntent.DATA_QUERY;
339
+ return {
340
+ intent: tentative,
341
+ metaScore,
342
+ dataScore,
343
+ ambiguous: true,
344
+ };
345
+ }
346
+
347
+ return {
348
+ intent: hasServices
349
+ ? LegendAIQuestionIntent.DATA_QUERY
350
+ : LegendAIQuestionIntent.METADATA,
351
+ metaScore,
352
+ dataScore,
353
+ ambiguous: true,
354
+ };
355
+ }
356
+
357
+ /**
358
+ * Legacy synchronous classifier for backward compatibility.
359
+ * Returns just the intent, discarding ambiguity info.
360
+ */
361
+ export function classifyQuestionIntent(
362
+ question: string,
363
+ hasServices: boolean,
364
+ ): LegendAIQuestionIntent {
365
+ return classifyQuestionIntentFast(question, hasServices).intent;
366
+ }
367
+
368
+ export interface LegendAIChatProps {
369
+ services: TDSServiceSchema[];
370
+ coordinates: string;
371
+ config: LegendAIConfig;
372
+ metadata: LegendAIProductMetadata;
373
+ title?: string;
374
+ plugin: LegendAI_LegendApplicationPlugin_Extension;
375
+ /**
376
+ * Structured data product coordinates for the Legend AI Orchestrator flow.
377
+ * When provided alongside an orchestratorUrl in config, enables ad-hoc
378
+ * query generation via entity resolution + orchestrator.
379
+ */
380
+ dataProductCoordinates?: LegendAIOrchestratorDataProductCoordinates;
381
+ /**
382
+ * Execution context (mapping + runtime) for executing Pure queries
383
+ * returned by the orchestrator. Required for the execute-after-generate flow.
384
+ */
385
+ pureExecutionContext?: QueryExplicitExecutionContextInfo;
386
+ }
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Copyright (c) 2026-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import {
18
+ LegendApplicationPlugin,
19
+ type LegendApplicationPluginManager,
20
+ } from '@finos/legend-application';
21
+ import type {
22
+ QueryExplicitExecutionContextInfo,
23
+ TDSRowDataType,
24
+ } from '@finos/legend-graph';
25
+ import type {
26
+ TDSServiceSchema,
27
+ LegendAIConfig,
28
+ LegendAIConversationTurn,
29
+ LegendAIProductMetadata,
30
+ LegendAIQuestionIntent,
31
+ } from './LegendAITypes.js';
32
+
33
+ export class LegendAISqlExtractionResult {
34
+ sql!: string | null;
35
+ failure!: string | null;
36
+ suggestion?: string;
37
+ }
38
+
39
+ export enum LegendAIJudgeVerdict {
40
+ PASS = 'PASS',
41
+ FAIL = 'FAIL',
42
+ }
43
+
44
+ export class LegendAIJudgeResult {
45
+ verdict!: LegendAIJudgeVerdict;
46
+ issues?: string;
47
+ correctedSql?: string;
48
+ }
49
+
50
+ export class LegendAISqlExecutionResultData {
51
+ columns!: string[];
52
+ rows!: TDSRowDataType[];
53
+ }
54
+
55
+ export class LegendAIOrchestratorDataProductCoordinates {
56
+ data_product!: string;
57
+ group_id!: string;
58
+ artifact_id!: string;
59
+ version!: string;
60
+ }
61
+
62
+ export interface LegendAISemanticSearchResolutionDetails {
63
+ data_product_coordinates: LegendAIOrchestratorDataProductCoordinates;
64
+ root_entity: string;
65
+ related_entities: string[];
66
+ }
67
+
68
+ export class LegendAIOrchestratorRequest {
69
+ user_question!: string;
70
+ semantic_search_resolution_details!: LegendAISemanticSearchResolutionDetails;
71
+ }
72
+
73
+ export class LegendAIOrchestratorResponse {
74
+ legend_query!: string;
75
+ }
76
+
77
+ export class LegendAIResolvedEntities {
78
+ rootEntity!: string;
79
+ relatedEntities!: string[];
80
+ }
81
+
82
+ /**
83
+ * @deprecated Use {@link QueryExplicitExecutionContextInfo} from `@finos/legend-graph` directly.
84
+ */
85
+ export type LegendAIPureExecutionContext = QueryExplicitExecutionContextInfo;
86
+
87
+ export abstract class LegendAI_LegendApplicationPlugin_Extension extends LegendApplicationPlugin {
88
+ /**
89
+ * This helps to better type-check for this empty abstract type
90
+ * See https://github.com/finos/legend-studio/blob/master/docs/technical/typescript-usage.md#understand-typescript-structual-type-system
91
+ */
92
+ private readonly _$nominalTypeBrand!: 'LegendAI_LegendApplicationPlugin_Extension';
93
+
94
+ install(
95
+ pluginManager: LegendApplicationPluginManager<LegendApplicationPlugin>,
96
+ ): void {
97
+ pluginManager.registerApplicationPlugin(this);
98
+ }
99
+
100
+ /**
101
+ * Classify the user question as either a data query or a metadata question.
102
+ *
103
+ * This is async to allow implementations to use an LLM micro-classifier
104
+ * for ambiguous questions while falling back to fast regex for clear-cut cases.
105
+ */
106
+ abstract classifyQuestionIntent(
107
+ question: string,
108
+ hasServices: boolean,
109
+ config: LegendAIConfig,
110
+ serviceNames?: string[],
111
+ ): Promise<LegendAIQuestionIntent>;
112
+
113
+ /**
114
+ * Build the LLM prompt for answering a metadata question about the product.
115
+ */
116
+ abstract buildMetadataPrompt(
117
+ question: string,
118
+ metadata: LegendAIProductMetadata,
119
+ history?: LegendAIConversationTurn[],
120
+ ): string;
121
+
122
+ /**
123
+ * Build the LLM prompt for generating a SQL query from the user question.
124
+ */
125
+ abstract buildGeneratorPrompt(
126
+ question: string,
127
+ services: TDSServiceSchema[],
128
+ coordinates: string,
129
+ history?: LegendAIConversationTurn[],
130
+ ): string;
131
+
132
+ /**
133
+ * Build the LLM prompt for verifying and correcting a generated SQL query.
134
+ */
135
+ abstract buildJudgePrompt(
136
+ sql: string,
137
+ question: string,
138
+ services: TDSServiceSchema[],
139
+ coordinates: string,
140
+ history?: LegendAIConversationTurn[],
141
+ ): string;
142
+
143
+ /**
144
+ * Send a prompt to the LLM service and return the raw response text.
145
+ */
146
+ abstract callLLM(prompt: string, config: LegendAIConfig): Promise<string>;
147
+
148
+ /**
149
+ * Execute a SQL query against the data platform and return tabular results.
150
+ */
151
+ abstract executeSql(
152
+ sql: string,
153
+ config: LegendAIConfig,
154
+ ): Promise<LegendAISqlExecutionResultData>;
155
+
156
+ /**
157
+ * Parse the LLM generator response to extract the SQL query or failure reason.
158
+ */
159
+ abstract extractSqlFromResponse(
160
+ answerText: string,
161
+ ): LegendAISqlExtractionResult;
162
+
163
+ /**
164
+ * Parse the LLM judge response to extract the verdict and optional corrections.
165
+ */
166
+ abstract extractJudgeResult(answerText: string): LegendAIJudgeResult;
167
+
168
+ /**
169
+ * Call the Legend AI Orchestrator to generate a Legend/PURE query from
170
+ * natural language using resolved entity information.
171
+ */
172
+ abstract generateQueryViaOrchestrator(
173
+ request: LegendAIOrchestratorRequest,
174
+ config: LegendAIConfig,
175
+ ): Promise<LegendAIOrchestratorResponse>;
176
+
177
+ /**
178
+ * Resolve root and related entities for a given user query within a data product.
179
+ * Uses the dataset search API to determine which entities are most relevant.
180
+ */
181
+ abstract resolveEntitiesForQuery(
182
+ query: string,
183
+ dataProductCoordinates: LegendAIOrchestratorDataProductCoordinates,
184
+ config: LegendAIConfig,
185
+ ): Promise<LegendAIResolvedEntities>;
186
+
187
+ /**
188
+ * Execute a Pure/Legend query against the engine and return tabular results.
189
+ * Converts the Pure expression text to a lambda, then executes it using
190
+ * the engine's execution endpoint with the provided execution context.
191
+ */
192
+ abstract executePureQuery(
193
+ pureQuery: string,
194
+ executionContext: QueryExplicitExecutionContextInfo,
195
+ dataProductCoordinates: LegendAIOrchestratorDataProductCoordinates,
196
+ config: LegendAIConfig,
197
+ ): Promise<LegendAISqlExecutionResultData>;
198
+
199
+ /**
200
+ * Execute a SQL query that uses `p()` syntax against data product access points.
201
+ * Wraps the SQL in `#SQL{...}#`, builds a lakehouse runtime, and executes via the engine.
202
+ */
203
+ abstract executeLakehouseSql(
204
+ sql: string,
205
+ dataProductCoordinates: LegendAIOrchestratorDataProductCoordinates,
206
+ config: LegendAIConfig,
207
+ ): Promise<LegendAISqlExecutionResultData>;
208
+ }