@finos/legend-application-marketplace 0.2.14 → 0.2.16
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.
- package/lib/__lib__/LegendMarketplaceNavigation.d.ts +2 -0
- package/lib/__lib__/LegendMarketplaceNavigation.d.ts.map +1 -1
- package/lib/__lib__/LegendMarketplaceNavigation.js +4 -0
- package/lib/__lib__/LegendMarketplaceNavigation.js.map +1 -1
- package/lib/application/LegendMarketplaceApplicationConfig.d.ts +9 -0
- package/lib/application/LegendMarketplaceApplicationConfig.d.ts.map +1 -1
- package/lib/application/LegendMarketplaceApplicationConfig.js +40 -22
- package/lib/application/LegendMarketplaceApplicationConfig.js.map +1 -1
- package/lib/application/LegendMarketplaceWebApplication.d.ts.map +1 -1
- package/lib/application/LegendMarketplaceWebApplication.js +4 -1
- package/lib/application/LegendMarketplaceWebApplication.js.map +1 -1
- package/lib/application/__test-utils__/LegendMarketplaceApplicationTestUtils.d.ts.map +1 -1
- package/lib/application/__test-utils__/LegendMarketplaceApplicationTestUtils.js +7 -0
- package/lib/application/__test-utils__/LegendMarketplaceApplicationTestUtils.js.map +1 -1
- package/lib/application/providers/LegendMarketplaceAIChatStoreProvider.d.ts +21 -0
- package/lib/application/providers/LegendMarketplaceAIChatStoreProvider.d.ts.map +1 -0
- package/lib/application/providers/LegendMarketplaceAIChatStoreProvider.js +29 -0
- package/lib/application/providers/LegendMarketplaceAIChatStoreProvider.js.map +1 -0
- package/lib/components/Header/LegendMarketplaceIconToolbar.d.ts.map +1 -1
- package/lib/components/Header/LegendMarketplaceIconToolbar.js +4 -3
- package/lib/components/Header/LegendMarketplaceIconToolbar.js.map +1 -1
- package/lib/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/package.json +1 -1
- package/lib/pages/Agents/LegendMarketplaceAgents.d.ts.map +1 -1
- package/lib/pages/Agents/LegendMarketplaceAgents.js +49 -17
- package/lib/pages/Agents/LegendMarketplaceAgents.js.map +1 -1
- package/lib/pages/Agents/MarketplaceAIChatView.d.ts +21 -0
- package/lib/pages/Agents/MarketplaceAIChatView.d.ts.map +1 -0
- package/lib/pages/Agents/MarketplaceAIChatView.js +141 -0
- package/lib/pages/Agents/MarketplaceAIChatView.js.map +1 -0
- package/lib/pages/Agents/MarketplaceAIInputBar.d.ts +22 -0
- package/lib/pages/Agents/MarketplaceAIInputBar.d.ts.map +1 -0
- package/lib/pages/Agents/MarketplaceAIInputBar.js +40 -0
- package/lib/pages/Agents/MarketplaceAIInputBar.js.map +1 -0
- package/lib/pages/Agents/MarketplaceAIProductAutosuggest.d.ts +25 -0
- package/lib/pages/Agents/MarketplaceAIProductAutosuggest.d.ts.map +1 -0
- package/lib/pages/Agents/MarketplaceAIProductAutosuggest.js +86 -0
- package/lib/pages/Agents/MarketplaceAIProductAutosuggest.js.map +1 -0
- package/lib/pages/Agents/MarketplaceAIProductCards.d.ts +23 -0
- package/lib/pages/Agents/MarketplaceAIProductCards.d.ts.map +1 -0
- package/lib/pages/Agents/MarketplaceAIProductCards.js +20 -0
- package/lib/pages/Agents/MarketplaceAIProductCards.js.map +1 -0
- package/lib/pages/Agents/MarketplaceAIScopeSelector.d.ts +19 -0
- package/lib/pages/Agents/MarketplaceAIScopeSelector.d.ts.map +1 -0
- package/lib/pages/Agents/MarketplaceAIScopeSelector.js +46 -0
- package/lib/pages/Agents/MarketplaceAIScopeSelector.js.map +1 -0
- package/lib/pages/Lakehouse/entitlements/EntitlementsClosedContractsDashboard.d.ts.map +1 -1
- package/lib/pages/Lakehouse/entitlements/EntitlementsClosedContractsDashboard.js +70 -56
- package/lib/pages/Lakehouse/entitlements/EntitlementsClosedContractsDashboard.js.map +1 -1
- package/lib/pages/Lakehouse/entitlements/EntitlementsPendingContractsDashboard.d.ts.map +1 -1
- package/lib/pages/Lakehouse/entitlements/EntitlementsPendingContractsDashboard.js +82 -62
- package/lib/pages/Lakehouse/entitlements/EntitlementsPendingContractsDashboard.js.map +1 -1
- package/lib/pages/Lakehouse/entitlements/PermitDataAccessRequest.d.ts +17 -0
- package/lib/pages/Lakehouse/entitlements/PermitDataAccessRequest.d.ts.map +1 -0
- package/lib/pages/Lakehouse/entitlements/PermitDataAccessRequest.js +126 -0
- package/lib/pages/Lakehouse/entitlements/PermitDataAccessRequest.js.map +1 -0
- package/lib/pages/Lakehouse/entitlements/WorkflowDataAccessRequest.d.ts.map +1 -1
- package/lib/pages/Lakehouse/entitlements/WorkflowDataAccessRequest.js +23 -65
- package/lib/pages/Lakehouse/entitlements/WorkflowDataAccessRequest.js.map +1 -1
- package/lib/pages/Lakehouse/entitlements/showTaskActionAlert.d.ts +29 -0
- package/lib/pages/Lakehouse/entitlements/showTaskActionAlert.d.ts.map +1 -0
- package/lib/pages/Lakehouse/entitlements/showTaskActionAlert.js +60 -0
- package/lib/pages/Lakehouse/entitlements/showTaskActionAlert.js.map +1 -0
- package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.d.ts.map +1 -1
- package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.js +3 -8
- package/lib/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.js.map +1 -1
- package/lib/stores/LegendMarketplaceBaseStore.d.ts +2 -1
- package/lib/stores/LegendMarketplaceBaseStore.d.ts.map +1 -1
- package/lib/stores/LegendMarketplaceBaseStore.js +8 -1
- package/lib/stores/LegendMarketplaceBaseStore.js.map +1 -1
- package/lib/stores/ai/LegendMarketplaceAIChatStore.d.ts +109 -0
- package/lib/stores/ai/LegendMarketplaceAIChatStore.d.ts.map +1 -0
- package/lib/stores/ai/LegendMarketplaceAIChatStore.js +1106 -0
- package/lib/stores/ai/LegendMarketplaceAIChatStore.js.map +1 -0
- package/lib/stores/lakehouse/LegendMarketplaceProductViewerStore.d.ts.map +1 -1
- package/lib/stores/lakehouse/LegendMarketplaceProductViewerStore.js +3 -2
- package/lib/stores/lakehouse/LegendMarketplaceProductViewerStore.js.map +1 -1
- package/lib/stores/lakehouse/dataProducts/ProductCardState.d.ts +1 -1
- package/lib/stores/lakehouse/dataProducts/ProductCardState.d.ts.map +1 -1
- package/lib/stores/lakehouse/dataProducts/ProductCardState.js +1 -2
- package/lib/stores/lakehouse/dataProducts/ProductCardState.js.map +1 -1
- package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.d.ts +4 -1
- package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.d.ts.map +1 -1
- package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.js +22 -2
- package/lib/stores/lakehouse/entitlements/EntitlementsDashboardState.js.map +1 -1
- package/lib/utils/EntitlementsUtils.d.ts +36 -2
- package/lib/utils/EntitlementsUtils.d.ts.map +1 -1
- package/lib/utils/EntitlementsUtils.js +225 -46
- package/lib/utils/EntitlementsUtils.js.map +1 -1
- package/lib/utils/SearchUtils.d.ts.map +1 -1
- package/lib/utils/SearchUtils.js +7 -4
- package/lib/utils/SearchUtils.js.map +1 -1
- package/package.json +10 -10
- package/src/__lib__/LegendMarketplaceNavigation.ts +11 -0
- package/src/application/LegendMarketplaceApplicationConfig.ts +62 -24
- package/src/application/LegendMarketplaceWebApplication.tsx +15 -0
- package/src/application/__test-utils__/LegendMarketplaceApplicationTestUtils.ts +7 -0
- package/src/application/providers/LegendMarketplaceAIChatStoreProvider.tsx +47 -0
- package/src/components/Header/LegendMarketplaceIconToolbar.tsx +4 -3
- package/src/pages/Agents/LegendMarketplaceAgents.tsx +145 -23
- package/src/pages/Agents/MarketplaceAIChatView.tsx +555 -0
- package/src/pages/Agents/MarketplaceAIInputBar.tsx +91 -0
- package/src/pages/Agents/MarketplaceAIProductAutosuggest.tsx +181 -0
- package/src/pages/Agents/MarketplaceAIProductCards.tsx +111 -0
- package/src/pages/Agents/MarketplaceAIScopeSelector.tsx +84 -0
- package/src/pages/Lakehouse/entitlements/EntitlementsClosedContractsDashboard.tsx +124 -127
- package/src/pages/Lakehouse/entitlements/EntitlementsPendingContractsDashboard.tsx +151 -153
- package/src/pages/Lakehouse/entitlements/PermitDataAccessRequest.tsx +245 -0
- package/src/pages/Lakehouse/entitlements/WorkflowDataAccessRequest.tsx +25 -94
- package/src/pages/Lakehouse/entitlements/showTaskActionAlert.tsx +101 -0
- package/src/pages/Lakehouse/searchResults/LegendMarketplaceSearchResults.tsx +27 -31
- package/src/stores/LegendMarketplaceBaseStore.ts +12 -0
- package/src/stores/ai/LegendMarketplaceAIChatStore.ts +1720 -0
- package/src/stores/lakehouse/LegendMarketplaceProductViewerStore.ts +6 -0
- package/src/stores/lakehouse/dataProducts/ProductCardState.ts +3 -4
- package/src/stores/lakehouse/entitlements/EntitlementsDashboardState.ts +69 -30
- package/src/utils/EntitlementsUtils.tsx +341 -86
- package/src/utils/SearchUtils.tsx +7 -4
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,1106 @@
|
|
|
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
|
+
import { action, computed, flow, makeObservable, observable, runInAction, } from 'mobx';
|
|
17
|
+
import { ActionState, assertErrorThrown, guaranteeNonNullable, } from '@finos/legend-shared';
|
|
18
|
+
import { LegendAIMessageRole, LegendAIQuestionIntent, LegendAIResolvedEntities, TDSServiceSourceType, findLegendAIPlugin, processQuestionViaOrchestrator, handleMetadataQuestion, generateAndJudgeSql, executeSqlAndReport, analyzeOrchestratorResults, addThinkingStep, completeThinkingSteps, finishWithThinkingError, classifyError, updateLastAssistant, buildConversationHistory, createMessagePair, elapsedSeconds, LEGEND_AI_ORCHESTRATOR_FALLBACK_ACTION_ID, } from '@finos/legend-lego/legend-ai';
|
|
19
|
+
import { QueryExplicitExecutionContextInfo } from '@finos/legend-graph';
|
|
20
|
+
import { V1_deserializeDataSpace, } from '@finos/legend-extension-dsl-data-space/graph';
|
|
21
|
+
import { convertAutosuggestResultToSearchResult } from '../../utils/SearchUtils.js';
|
|
22
|
+
import { DataProductSearchResult, DataProductSearchResponse, DataProductDetailsType, DataProductSearchResultDetailsType, FieldSearchType, GroupedFieldSearchResponse, LakehouseDataProductSearchResultDetails, LakehouseSDLCDataProductSearchResultOrigin, LegacyDataProductSearchResultDetails, EntitySearchResponse, } from '@finos/legend-server-marketplace';
|
|
23
|
+
export var MarketplaceAIChatStage;
|
|
24
|
+
(function (MarketplaceAIChatStage) {
|
|
25
|
+
MarketplaceAIChatStage["IDLE"] = "idle";
|
|
26
|
+
MarketplaceAIChatStage["SEARCHING"] = "searching";
|
|
27
|
+
MarketplaceAIChatStage["PRODUCT_SELECTION"] = "product-selection";
|
|
28
|
+
MarketplaceAIChatStage["QUERYING"] = "querying";
|
|
29
|
+
MarketplaceAIChatStage["RESULTS"] = "results";
|
|
30
|
+
})(MarketplaceAIChatStage || (MarketplaceAIChatStage = {}));
|
|
31
|
+
const FIELD_COVERAGE_BOOST = 0.6;
|
|
32
|
+
const MAX_PRODUCT_SUGGESTIONS = 3;
|
|
33
|
+
const MERGED_CANDIDATE_LIMIT = 6;
|
|
34
|
+
const PRODUCT_SEARCH_PAGE_SIZE = 6;
|
|
35
|
+
const FIELD_SEARCH_PAGE_SIZE = 5;
|
|
36
|
+
const MAX_RELEVANT_SERVICES = 5;
|
|
37
|
+
const DESCRIPTION_PREVIEW_LENGTH = 200;
|
|
38
|
+
const DATASET_SEARCH_PAGE_SIZE = 20;
|
|
39
|
+
const DEFAULT_SUGGESTED_QUERIES = [
|
|
40
|
+
'What BVAL bond pricing data is available?',
|
|
41
|
+
'Show me credit risk data products',
|
|
42
|
+
'Find FX rates and currency data',
|
|
43
|
+
'What equity analytics data do we have?',
|
|
44
|
+
];
|
|
45
|
+
export function unwrapProductDetails(product) {
|
|
46
|
+
const details = product.dataProductDetails;
|
|
47
|
+
if (details instanceof LegacyDataProductSearchResultDetails) {
|
|
48
|
+
return {
|
|
49
|
+
groupId: details.groupId,
|
|
50
|
+
artifactId: details.artifactId,
|
|
51
|
+
versionId: details.versionId,
|
|
52
|
+
path: details.path,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (details instanceof LakehouseDataProductSearchResultDetails) {
|
|
56
|
+
const origin = details.origin;
|
|
57
|
+
if (origin instanceof LakehouseSDLCDataProductSearchResultOrigin) {
|
|
58
|
+
return {
|
|
59
|
+
groupId: origin.groupId ?? '',
|
|
60
|
+
artifactId: origin.artifactId ?? '',
|
|
61
|
+
versionId: origin.versionId ?? '',
|
|
62
|
+
path: origin.path ?? '',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return { groupId: '', artifactId: '', versionId: '', path: '' };
|
|
67
|
+
}
|
|
68
|
+
export class LegendMarketplaceAIChatStore {
|
|
69
|
+
baseStore;
|
|
70
|
+
stage = MarketplaceAIChatStage.IDLE;
|
|
71
|
+
questionText = '';
|
|
72
|
+
messages = [];
|
|
73
|
+
isSending = false;
|
|
74
|
+
suggestedProducts = [];
|
|
75
|
+
scoredCandidates = [];
|
|
76
|
+
scopeProducts = [];
|
|
77
|
+
selectedProduct = undefined;
|
|
78
|
+
selectedProductCoordinates = undefined;
|
|
79
|
+
selectedProductMetadata = undefined;
|
|
80
|
+
pureExecutionContext = undefined;
|
|
81
|
+
pendingFallbackQuestion = undefined;
|
|
82
|
+
resolvedProductServices = [];
|
|
83
|
+
lastResolvedEntities = undefined;
|
|
84
|
+
lastEntityCandidates = [];
|
|
85
|
+
selectedDataProductId = undefined;
|
|
86
|
+
searchState = ActionState.create();
|
|
87
|
+
constructor(baseStore) {
|
|
88
|
+
makeObservable(this, {
|
|
89
|
+
stage: observable,
|
|
90
|
+
questionText: observable,
|
|
91
|
+
messages: observable,
|
|
92
|
+
isSending: observable,
|
|
93
|
+
suggestedProducts: observable,
|
|
94
|
+
scoredCandidates: observable,
|
|
95
|
+
scopeProducts: observable,
|
|
96
|
+
selectedProduct: observable,
|
|
97
|
+
selectedProductCoordinates: observable,
|
|
98
|
+
selectedProductMetadata: observable,
|
|
99
|
+
pureExecutionContext: observable,
|
|
100
|
+
pendingFallbackQuestion: observable,
|
|
101
|
+
resolvedProductServices: observable,
|
|
102
|
+
lastResolvedEntities: observable,
|
|
103
|
+
lastEntityCandidates: observable,
|
|
104
|
+
selectedDataProductId: observable,
|
|
105
|
+
setQuestionText: action,
|
|
106
|
+
setStage: action,
|
|
107
|
+
clearChat: action,
|
|
108
|
+
selectDataProduct: action,
|
|
109
|
+
selectAutosuggestProduct: action,
|
|
110
|
+
deselectProduct: action,
|
|
111
|
+
addScopeProduct: action,
|
|
112
|
+
removeScopeProduct: action,
|
|
113
|
+
submitQuery: flow,
|
|
114
|
+
askFollowUp: flow,
|
|
115
|
+
runOrchestratorFallback: flow,
|
|
116
|
+
config: computed,
|
|
117
|
+
plugin: computed,
|
|
118
|
+
isEnabled: computed,
|
|
119
|
+
lastUserMessageText: computed,
|
|
120
|
+
welcomeSuggestedQueries: computed,
|
|
121
|
+
});
|
|
122
|
+
this.baseStore = baseStore;
|
|
123
|
+
}
|
|
124
|
+
get config() {
|
|
125
|
+
return this.baseStore.applicationStore.config.legendAIConfig;
|
|
126
|
+
}
|
|
127
|
+
get plugin() {
|
|
128
|
+
return findLegendAIPlugin(this.baseStore.pluginManager.getApplicationPlugins());
|
|
129
|
+
}
|
|
130
|
+
get isEnabled() {
|
|
131
|
+
return this.config.enabled && this.plugin !== undefined;
|
|
132
|
+
}
|
|
133
|
+
get lastUserMessageText() {
|
|
134
|
+
return (this.messages.findLast((m) => m.role === LegendAIMessageRole.USER)
|
|
135
|
+
?.text ?? '');
|
|
136
|
+
}
|
|
137
|
+
get welcomeSuggestedQueries() {
|
|
138
|
+
return (this.baseStore.applicationStore.config.options
|
|
139
|
+
.defaultAISuggestedQueries ?? DEFAULT_SUGGESTED_QUERIES);
|
|
140
|
+
}
|
|
141
|
+
setQuestionText(text) {
|
|
142
|
+
this.questionText = text;
|
|
143
|
+
}
|
|
144
|
+
setStage(stage) {
|
|
145
|
+
this.stage = stage;
|
|
146
|
+
}
|
|
147
|
+
clearChat() {
|
|
148
|
+
this.messages = [];
|
|
149
|
+
this.suggestedProducts = [];
|
|
150
|
+
this.scoredCandidates = [];
|
|
151
|
+
this.selectedProduct = undefined;
|
|
152
|
+
const firstScope = this.scopeProducts[0];
|
|
153
|
+
this.selectedProductCoordinates = firstScope?.coordinates;
|
|
154
|
+
this.selectedProductMetadata = firstScope
|
|
155
|
+
? {
|
|
156
|
+
name: firstScope.name,
|
|
157
|
+
coordinates: `${firstScope.coordinates.group_id}:${firstScope.coordinates.artifact_id}:${firstScope.coordinates.version}`,
|
|
158
|
+
serviceSummaries: [],
|
|
159
|
+
}
|
|
160
|
+
: undefined;
|
|
161
|
+
this.pureExecutionContext = undefined;
|
|
162
|
+
this.pendingFallbackQuestion = undefined;
|
|
163
|
+
this.resolvedProductServices = [];
|
|
164
|
+
this.lastResolvedEntities = undefined;
|
|
165
|
+
this.lastEntityCandidates = [];
|
|
166
|
+
this.selectedDataProductId = undefined;
|
|
167
|
+
this.stage = MarketplaceAIChatStage.IDLE;
|
|
168
|
+
this.questionText = '';
|
|
169
|
+
this.isSending = false;
|
|
170
|
+
}
|
|
171
|
+
createMessageSetter() {
|
|
172
|
+
return (updater) => {
|
|
173
|
+
runInAction(() => {
|
|
174
|
+
if (typeof updater === 'function') {
|
|
175
|
+
this.messages = updater(this.messages);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
this.messages = updater;
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
buildContextPromise(question, metadata, setMessages) {
|
|
184
|
+
if (!this.plugin) {
|
|
185
|
+
return Promise.resolve();
|
|
186
|
+
}
|
|
187
|
+
return this.plugin
|
|
188
|
+
.buildDataContextSummary(question, metadata, this.config)
|
|
189
|
+
.then((contextText) => {
|
|
190
|
+
if (contextText) {
|
|
191
|
+
updateLastAssistant(setMessages, () => ({
|
|
192
|
+
dataContext: contextText,
|
|
193
|
+
}));
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
.catch(() => {
|
|
197
|
+
/* Non-fatal */
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
buildConversationHistory() {
|
|
201
|
+
return buildConversationHistory(this.messages);
|
|
202
|
+
}
|
|
203
|
+
extractMetadata(result, coordinates) {
|
|
204
|
+
const metadata = {
|
|
205
|
+
name: result.dataProductTitle ?? 'Unknown',
|
|
206
|
+
coordinates: `${coordinates.group_id}:${coordinates.artifact_id}:${coordinates.version}`,
|
|
207
|
+
serviceSummaries: [],
|
|
208
|
+
accessPointGroups: [],
|
|
209
|
+
};
|
|
210
|
+
if (result.dataProductDescription !== null) {
|
|
211
|
+
metadata.description = result.dataProductDescription;
|
|
212
|
+
}
|
|
213
|
+
const tags1 = result.tags1;
|
|
214
|
+
const tags2 = result.tags2;
|
|
215
|
+
if (tags1.length > 0 || tags2.length > 0) {
|
|
216
|
+
metadata.tags = [...tags1, ...tags2].map((t) => ({
|
|
217
|
+
profile: 'tag',
|
|
218
|
+
value: t,
|
|
219
|
+
}));
|
|
220
|
+
}
|
|
221
|
+
return metadata;
|
|
222
|
+
}
|
|
223
|
+
buildTitleFromPath(path, artifactId) {
|
|
224
|
+
const parts = path.split('::');
|
|
225
|
+
const filtered = parts.filter((p) => p.toLowerCase() !== 'dataspace' &&
|
|
226
|
+
p.toLowerCase() !== 'model' &&
|
|
227
|
+
!p.toLowerCase().endsWith('dataspace'));
|
|
228
|
+
if (filtered.length > 0) {
|
|
229
|
+
return filtered
|
|
230
|
+
.map((p) => p
|
|
231
|
+
.replaceAll(/(?<lower>[a-z])(?<upper>[A-Z])/g, '$<lower> $<upper>')
|
|
232
|
+
.replace(/^./, (c) => c.toUpperCase()))
|
|
233
|
+
.join(' ');
|
|
234
|
+
}
|
|
235
|
+
return artifactId
|
|
236
|
+
.split('-')
|
|
237
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
238
|
+
.join(' ');
|
|
239
|
+
}
|
|
240
|
+
async multiSignalSearch(question, setMessages) {
|
|
241
|
+
const env = this.baseStore.envState.lakehouseEnvironment;
|
|
242
|
+
addThinkingStep(setMessages, 'Searching products and fields in parallel...');
|
|
243
|
+
const [productRaw, fieldRaw] = await Promise.all([
|
|
244
|
+
this.baseStore.marketplaceServerClient.dataProductSearch(question, env, FieldSearchType.HYBRID, [], PRODUCT_SEARCH_PAGE_SIZE, 1, false),
|
|
245
|
+
this.baseStore.marketplaceServerClient
|
|
246
|
+
.fieldSearch(env, {
|
|
247
|
+
query: question,
|
|
248
|
+
searchType: FieldSearchType.HYBRID,
|
|
249
|
+
pageSize: FIELD_SEARCH_PAGE_SIZE,
|
|
250
|
+
pageNumber: 1,
|
|
251
|
+
})
|
|
252
|
+
.catch(() => null),
|
|
253
|
+
]);
|
|
254
|
+
const productResponse = DataProductSearchResponse.serialization.fromJson(productRaw);
|
|
255
|
+
const productResults = productResponse.results.filter((r) => r.dataProductDetails instanceof
|
|
256
|
+
LakehouseDataProductSearchResultDetails ||
|
|
257
|
+
r.dataProductDetails instanceof LegacyDataProductSearchResultDetails);
|
|
258
|
+
let fieldResults = [];
|
|
259
|
+
if (fieldRaw) {
|
|
260
|
+
try {
|
|
261
|
+
const fieldResponse = GroupedFieldSearchResponse.serialization.fromJson(fieldRaw);
|
|
262
|
+
fieldResults = fieldResponse.results;
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
/* Non-fatal: field search is best-effort */
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (fieldResults.length > 0) {
|
|
269
|
+
addThinkingStep(setMessages, `Found ${fieldResults.length} matching field${fieldResults.length > 1 ? 's' : ''} across products`);
|
|
270
|
+
}
|
|
271
|
+
return { productResults, fieldResults };
|
|
272
|
+
}
|
|
273
|
+
deriveProductsFromFieldResults(fieldResults, existingProducts) {
|
|
274
|
+
const existingKeys = new Set(existingProducts.map((p) => {
|
|
275
|
+
const { groupId, artifactId } = unwrapProductDetails(p);
|
|
276
|
+
return `${groupId}:${artifactId}`;
|
|
277
|
+
}));
|
|
278
|
+
const productFieldCounts = new Map();
|
|
279
|
+
for (const dp of fieldResults.flatMap((entry) => entry.dataProducts)) {
|
|
280
|
+
if (!dp.groupId || !dp.artifactId || !dp.versionId) {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
const key = `${dp.groupId}:${dp.artifactId}`;
|
|
284
|
+
if (existingKeys.has(key)) {
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
const existing = productFieldCounts.get(key);
|
|
288
|
+
if (existing) {
|
|
289
|
+
existing.fieldCount += 1;
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
productFieldCounts.set(key, {
|
|
293
|
+
path: dp.path,
|
|
294
|
+
productType: dp.productType,
|
|
295
|
+
groupId: dp.groupId,
|
|
296
|
+
artifactId: dp.artifactId,
|
|
297
|
+
versionId: dp.versionId,
|
|
298
|
+
...(dp.dataProductId === undefined
|
|
299
|
+
? {}
|
|
300
|
+
: { dataProductId: dp.dataProductId }),
|
|
301
|
+
fieldCount: 1,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const sorted = [...productFieldCounts.values()].sort((a, b) => b.fieldCount - a.fieldCount);
|
|
306
|
+
return sorted
|
|
307
|
+
.slice(0, MAX_PRODUCT_SUGGESTIONS)
|
|
308
|
+
.map((entry) => this.buildDerivedProduct(entry));
|
|
309
|
+
}
|
|
310
|
+
buildDerivedProduct(entry) {
|
|
311
|
+
const product = new DataProductSearchResult();
|
|
312
|
+
product.dataProductTitle = this.buildTitleFromPath(entry.path, entry.artifactId);
|
|
313
|
+
product.dataProductDescription = null;
|
|
314
|
+
product.tags1 = [];
|
|
315
|
+
product.tags2 = [];
|
|
316
|
+
product.tag_score = 0;
|
|
317
|
+
product.similarity = 0;
|
|
318
|
+
if (entry.productType === DataProductSearchResultDetailsType.LEGACY) {
|
|
319
|
+
const details = new LegacyDataProductSearchResultDetails();
|
|
320
|
+
details.groupId = entry.groupId;
|
|
321
|
+
details.artifactId = entry.artifactId;
|
|
322
|
+
details.versionId = entry.versionId;
|
|
323
|
+
details.path = entry.path;
|
|
324
|
+
product.dataProductDetails = details;
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
const origin = new LakehouseSDLCDataProductSearchResultOrigin();
|
|
328
|
+
origin.groupId = entry.groupId;
|
|
329
|
+
origin.artifactId = entry.artifactId;
|
|
330
|
+
origin.versionId = entry.versionId;
|
|
331
|
+
origin.path = entry.path;
|
|
332
|
+
const details = new LakehouseDataProductSearchResultDetails();
|
|
333
|
+
details.dataProductId = entry.dataProductId ?? '';
|
|
334
|
+
details.deploymentId = 0;
|
|
335
|
+
details.producerEnvironmentName = '';
|
|
336
|
+
details.producerEnvironmentType = undefined;
|
|
337
|
+
details.origin = origin;
|
|
338
|
+
product.dataProductDetails = details;
|
|
339
|
+
}
|
|
340
|
+
return product;
|
|
341
|
+
}
|
|
342
|
+
computeScoredCandidates(productResults, fieldResults) {
|
|
343
|
+
const allFieldNames = fieldResults.map((f) => f.fieldName);
|
|
344
|
+
const maxSimilarity = productResults.length > 0
|
|
345
|
+
? Math.max(...productResults.map((p) => p.similarity))
|
|
346
|
+
: 1;
|
|
347
|
+
const scoreProduct = (product) => {
|
|
348
|
+
const { groupId, artifactId, path: productPath, } = unwrapProductDetails(product);
|
|
349
|
+
const matchedFields = [];
|
|
350
|
+
const missingFields = [];
|
|
351
|
+
for (const fieldEntry of fieldResults) {
|
|
352
|
+
const inProduct = fieldEntry.dataProducts.some((dp) => dp.path === productPath ||
|
|
353
|
+
(dp.groupId &&
|
|
354
|
+
dp.artifactId &&
|
|
355
|
+
groupId === dp.groupId &&
|
|
356
|
+
artifactId === dp.artifactId) ||
|
|
357
|
+
(productPath.length > 0 && dp.path.includes(productPath)) ||
|
|
358
|
+
(dp.path.length > 0 && productPath.includes(dp.path)));
|
|
359
|
+
if (inProduct) {
|
|
360
|
+
matchedFields.push(fieldEntry.fieldName);
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
missingFields.push(fieldEntry.fieldName);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const productSimilarity = product.similarity;
|
|
367
|
+
const normalizedSimilarity = maxSimilarity > 0 ? productSimilarity / maxSimilarity : 0;
|
|
368
|
+
const fieldCoverage = allFieldNames.length > 0
|
|
369
|
+
? matchedFields.length / allFieldNames.length
|
|
370
|
+
: 0;
|
|
371
|
+
const fieldIntersection = allFieldNames.length > 0 && missingFields.length === 0 ? 1 : 0;
|
|
372
|
+
const compositeScore = allFieldNames.length > 0
|
|
373
|
+
? normalizedSimilarity + FIELD_COVERAGE_BOOST * fieldCoverage
|
|
374
|
+
: normalizedSimilarity;
|
|
375
|
+
return {
|
|
376
|
+
product,
|
|
377
|
+
productSimilarity,
|
|
378
|
+
fieldCoverage,
|
|
379
|
+
fieldIntersection,
|
|
380
|
+
matchedFields,
|
|
381
|
+
missingFields,
|
|
382
|
+
compositeScore,
|
|
383
|
+
};
|
|
384
|
+
};
|
|
385
|
+
// Score product search results
|
|
386
|
+
const productCandidates = productResults.map(scoreProduct);
|
|
387
|
+
productCandidates.sort((a, b) => b.compositeScore - a.compositeScore);
|
|
388
|
+
// Score field-derived products (discovered from field search, not in product search)
|
|
389
|
+
const fieldDerived = this.deriveProductsFromFieldResults(fieldResults, productResults);
|
|
390
|
+
const fieldCandidates = fieldDerived.map(scoreProduct);
|
|
391
|
+
fieldCandidates.sort((a, b) => b.fieldCoverage - a.fieldCoverage);
|
|
392
|
+
// Merge: interleave top product results with top field-derived results
|
|
393
|
+
// so both signals are represented in the final list
|
|
394
|
+
return this.mergeInterleaved(productCandidates, fieldCandidates, MERGED_CANDIDATE_LIMIT);
|
|
395
|
+
}
|
|
396
|
+
mergeInterleaved(productCandidates, fieldCandidates, limit) {
|
|
397
|
+
const merged = [];
|
|
398
|
+
const seenKeys = new Set();
|
|
399
|
+
let pIdx = 0;
|
|
400
|
+
let fIdx = 0;
|
|
401
|
+
const tryAdd = (candidate) => {
|
|
402
|
+
const { groupId, artifactId } = unwrapProductDetails(candidate.product);
|
|
403
|
+
const key = `${groupId}:${artifactId}`;
|
|
404
|
+
if (!seenKeys.has(key)) {
|
|
405
|
+
seenKeys.add(key);
|
|
406
|
+
merged.push(candidate);
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
while (merged.length < limit &&
|
|
410
|
+
(pIdx < productCandidates.length || fIdx < fieldCandidates.length)) {
|
|
411
|
+
// Add 2 from product search, then 1 from field-derived, repeat
|
|
412
|
+
const fromProduct = merged.length % 3 !== 2;
|
|
413
|
+
if (pIdx < productCandidates.length &&
|
|
414
|
+
(fromProduct || fIdx >= fieldCandidates.length)) {
|
|
415
|
+
tryAdd(guaranteeNonNullable(productCandidates[pIdx]));
|
|
416
|
+
pIdx++;
|
|
417
|
+
}
|
|
418
|
+
else if (fIdx < fieldCandidates.length) {
|
|
419
|
+
tryAdd(guaranteeNonNullable(fieldCandidates[fIdx]));
|
|
420
|
+
fIdx++;
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return merged;
|
|
427
|
+
}
|
|
428
|
+
async llmRerankProducts(question, candidates, fieldResults, setMessages) {
|
|
429
|
+
const plugin = this.plugin;
|
|
430
|
+
if (!plugin || candidates.length <= MAX_PRODUCT_SUGGESTIONS) {
|
|
431
|
+
return candidates.slice(0, MAX_PRODUCT_SUGGESTIONS);
|
|
432
|
+
}
|
|
433
|
+
addThinkingStep(setMessages, 'Using AI to rank best matching products...');
|
|
434
|
+
const candidateInputs = candidates.map((c) => ({
|
|
435
|
+
title: c.product.dataProductTitle ?? 'Unknown',
|
|
436
|
+
description: c.product.dataProductDescription
|
|
437
|
+
? c.product.dataProductDescription.slice(0, DESCRIPTION_PREVIEW_LENGTH)
|
|
438
|
+
: '',
|
|
439
|
+
matchedFields: c.matchedFields,
|
|
440
|
+
}));
|
|
441
|
+
const allFieldNames = fieldResults.map((f) => f.fieldName);
|
|
442
|
+
const indices = await plugin.rerankProducts(question, candidateInputs, allFieldNames, MAX_PRODUCT_SUGGESTIONS, this.config);
|
|
443
|
+
if (indices && indices.length > 0) {
|
|
444
|
+
return this.buildRankedList(indices, candidates, MAX_PRODUCT_SUGGESTIONS);
|
|
445
|
+
}
|
|
446
|
+
return candidates.slice(0, MAX_PRODUCT_SUGGESTIONS);
|
|
447
|
+
}
|
|
448
|
+
buildRankedList(indices, candidates, limit) {
|
|
449
|
+
const ranked = [];
|
|
450
|
+
for (const idx of indices) {
|
|
451
|
+
if (ranked.length >= limit) {
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
454
|
+
if (idx >= 0 && idx < candidates.length) {
|
|
455
|
+
ranked.push(guaranteeNonNullable(candidates[idx]));
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
for (const c of candidates) {
|
|
459
|
+
if (ranked.length >= limit) {
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
if (!ranked.includes(c)) {
|
|
463
|
+
ranked.push(c);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
return ranked;
|
|
467
|
+
}
|
|
468
|
+
*submitQuery(text) {
|
|
469
|
+
const trimmed = text.trim();
|
|
470
|
+
if (!trimmed || this.isSending || !this.plugin) {
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
this.isSending = true;
|
|
474
|
+
this.questionText = '';
|
|
475
|
+
this.messages = [...this.messages, ...createMessagePair(trimmed)];
|
|
476
|
+
const setMessages = this.createMessageSetter();
|
|
477
|
+
try {
|
|
478
|
+
if (this.selectedProductCoordinates) {
|
|
479
|
+
this.stage = MarketplaceAIChatStage.QUERYING;
|
|
480
|
+
const relevantDatasets = (yield this.enrichWithEntitySearch(trimmed, setMessages));
|
|
481
|
+
yield this.dispatchWithSql2(trimmed, relevantDatasets, setMessages);
|
|
482
|
+
this.stage = MarketplaceAIChatStage.RESULTS;
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
this.stage = MarketplaceAIChatStage.SEARCHING;
|
|
486
|
+
const { productResults, fieldResults } = (yield this.multiSignalSearch(trimmed, setMessages));
|
|
487
|
+
const candidates = this.computeScoredCandidates(productResults, fieldResults);
|
|
488
|
+
if (candidates.length === 0) {
|
|
489
|
+
completeThinkingSteps(setMessages);
|
|
490
|
+
updateLastAssistant(setMessages, () => ({
|
|
491
|
+
textAnswer: 'I could not find any data products matching your query. Please try rephrasing or use more specific terms.',
|
|
492
|
+
isProcessing: false,
|
|
493
|
+
}));
|
|
494
|
+
this.stage = MarketplaceAIChatStage.IDLE;
|
|
495
|
+
}
|
|
496
|
+
else {
|
|
497
|
+
const topCandidates = (yield this.llmRerankProducts(trimmed, candidates, fieldResults, setMessages));
|
|
498
|
+
const top = guaranteeNonNullable(topCandidates[0]);
|
|
499
|
+
addThinkingStep(setMessages, `Top candidate: ${top.product.dataProductTitle ?? 'Unknown'} (${(top.compositeScore * 100).toFixed(0)}% composite)`);
|
|
500
|
+
completeThinkingSteps(setMessages);
|
|
501
|
+
this.suggestedProducts = topCandidates.map((c) => c.product);
|
|
502
|
+
this.scoredCandidates = topCandidates;
|
|
503
|
+
const hasFieldInfo = fieldResults.length > 0 &&
|
|
504
|
+
topCandidates.some((c) => c.matchedFields.length > 0);
|
|
505
|
+
let message = `I found ${candidates.length} data product${candidates.length > 1 ? 's' : ''} that may contain the data you need.`;
|
|
506
|
+
if (hasFieldInfo) {
|
|
507
|
+
message += ' Field availability is shown for each product.';
|
|
508
|
+
}
|
|
509
|
+
message += ' Please select one to continue:';
|
|
510
|
+
updateLastAssistant(setMessages, () => ({
|
|
511
|
+
textAnswer: message,
|
|
512
|
+
isProcessing: false,
|
|
513
|
+
}));
|
|
514
|
+
this.stage = MarketplaceAIChatStage.PRODUCT_SELECTION;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
catch (error) {
|
|
518
|
+
assertErrorThrown(error);
|
|
519
|
+
finishWithThinkingError(setMessages, error.message, Date.now(), classifyError(error));
|
|
520
|
+
this.stage = MarketplaceAIChatStage.IDLE;
|
|
521
|
+
}
|
|
522
|
+
finally {
|
|
523
|
+
this.isSending = false;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
selectDataProduct(result) {
|
|
527
|
+
const { groupId, artifactId, versionId, path } = unwrapProductDetails(result);
|
|
528
|
+
if (!groupId || !artifactId || !versionId || !path) {
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
const coordinates = {
|
|
532
|
+
data_product: path,
|
|
533
|
+
group_id: groupId,
|
|
534
|
+
artifact_id: artifactId,
|
|
535
|
+
version: versionId,
|
|
536
|
+
};
|
|
537
|
+
this.selectedProduct = result;
|
|
538
|
+
this.selectedProductCoordinates = coordinates;
|
|
539
|
+
this.selectedProductMetadata = this.extractMetadata(result, coordinates);
|
|
540
|
+
this.suggestedProducts = [];
|
|
541
|
+
const details = result.dataProductDetails;
|
|
542
|
+
if (details instanceof LakehouseDataProductSearchResultDetails) {
|
|
543
|
+
this.selectedDataProductId = details.dataProductId;
|
|
544
|
+
}
|
|
545
|
+
else {
|
|
546
|
+
this.selectedDataProductId = undefined;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
selectAutosuggestProduct(result) {
|
|
550
|
+
const searchResult = convertAutosuggestResultToSearchResult(result);
|
|
551
|
+
this.selectDataProduct(searchResult);
|
|
552
|
+
}
|
|
553
|
+
deselectProduct() {
|
|
554
|
+
this.selectedProduct = undefined;
|
|
555
|
+
this.selectedProductCoordinates = undefined;
|
|
556
|
+
this.selectedProductMetadata = undefined;
|
|
557
|
+
this.pureExecutionContext = undefined;
|
|
558
|
+
this.resolvedProductServices = [];
|
|
559
|
+
this.lastResolvedEntities = undefined;
|
|
560
|
+
this.lastEntityCandidates = [];
|
|
561
|
+
this.selectedDataProductId = undefined;
|
|
562
|
+
this.stage = MarketplaceAIChatStage.PRODUCT_SELECTION;
|
|
563
|
+
}
|
|
564
|
+
addScopeProduct(result) {
|
|
565
|
+
const details = result.dataProductDetails;
|
|
566
|
+
let groupId;
|
|
567
|
+
let artifactId;
|
|
568
|
+
let versionId;
|
|
569
|
+
let path;
|
|
570
|
+
if (details._type === DataProductDetailsType.LAKEHOUSE &&
|
|
571
|
+
details.origin !== undefined) {
|
|
572
|
+
groupId = details.origin.groupId;
|
|
573
|
+
artifactId = details.origin.artifactId;
|
|
574
|
+
versionId = details.origin.versionId;
|
|
575
|
+
path = details.origin.path;
|
|
576
|
+
}
|
|
577
|
+
else {
|
|
578
|
+
groupId = details.groupId;
|
|
579
|
+
artifactId = details.artifactId;
|
|
580
|
+
versionId = details.versionId;
|
|
581
|
+
path = details.path;
|
|
582
|
+
}
|
|
583
|
+
if (!groupId || !artifactId || !versionId || !path) {
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
const key = `${groupId}:${artifactId}:${versionId}`;
|
|
587
|
+
if (this.scopeProducts.some((p) => `${p.coordinates.group_id}:${p.coordinates.artifact_id}:${p.coordinates.version}` ===
|
|
588
|
+
key)) {
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
if (this.scopeProducts.length >= 3) {
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
const coords = {
|
|
595
|
+
data_product: path,
|
|
596
|
+
group_id: groupId,
|
|
597
|
+
artifact_id: artifactId,
|
|
598
|
+
version: versionId,
|
|
599
|
+
};
|
|
600
|
+
this.scopeProducts = [
|
|
601
|
+
...this.scopeProducts,
|
|
602
|
+
{ name: result.dataProductName, coordinates: coords },
|
|
603
|
+
];
|
|
604
|
+
if (this.scopeProducts.length === 1) {
|
|
605
|
+
this.selectedProductCoordinates = coords;
|
|
606
|
+
this.selectedProductMetadata = {
|
|
607
|
+
name: result.dataProductName,
|
|
608
|
+
description: result.dataProductDescription,
|
|
609
|
+
coordinates: key,
|
|
610
|
+
serviceSummaries: [],
|
|
611
|
+
};
|
|
612
|
+
this.selectedDataProductId = details.dataProductId;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
removeScopeProduct(index) {
|
|
616
|
+
this.scopeProducts = this.scopeProducts.filter((_, i) => i !== index);
|
|
617
|
+
if (this.selectedProduct === undefined) {
|
|
618
|
+
const firstScope = this.scopeProducts[0];
|
|
619
|
+
this.selectedProductCoordinates = firstScope?.coordinates;
|
|
620
|
+
this.selectedProductMetadata = firstScope
|
|
621
|
+
? {
|
|
622
|
+
name: firstScope.name,
|
|
623
|
+
coordinates: `${firstScope.coordinates.group_id}:${firstScope.coordinates.artifact_id}:${firstScope.coordinates.version}`,
|
|
624
|
+
serviceSummaries: [],
|
|
625
|
+
}
|
|
626
|
+
: undefined;
|
|
627
|
+
this.pureExecutionContext = undefined;
|
|
628
|
+
this.resolvedProductServices = [];
|
|
629
|
+
this.lastResolvedEntities = undefined;
|
|
630
|
+
this.lastEntityCandidates = [];
|
|
631
|
+
this.selectedDataProductId = undefined;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
async resolveExecutionContext(setMessages) {
|
|
635
|
+
const product = this.selectedProduct;
|
|
636
|
+
const coordinates = this.selectedProductCoordinates;
|
|
637
|
+
if (!coordinates) {
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
addThinkingStep(setMessages, 'Resolving execution context...');
|
|
641
|
+
try {
|
|
642
|
+
let dataSpace;
|
|
643
|
+
if (product) {
|
|
644
|
+
const details = product.dataProductDetails;
|
|
645
|
+
if (details instanceof LegacyDataProductSearchResultDetails) {
|
|
646
|
+
const entity = await this.baseStore.depotServerClient.getVersionEntity(details.groupId, details.artifactId, details.versionId, details.path);
|
|
647
|
+
dataSpace = V1_deserializeDataSpace(entity.content);
|
|
648
|
+
}
|
|
649
|
+
else if (details instanceof LakehouseDataProductSearchResultDetails &&
|
|
650
|
+
details.origin instanceof
|
|
651
|
+
LakehouseSDLCDataProductSearchResultOrigin &&
|
|
652
|
+
details.origin.groupId &&
|
|
653
|
+
details.origin.artifactId &&
|
|
654
|
+
details.origin.versionId &&
|
|
655
|
+
details.origin.path) {
|
|
656
|
+
const entity = await this.baseStore.depotServerClient.getVersionEntity(details.origin.groupId, details.origin.artifactId, details.origin.versionId, details.origin.path);
|
|
657
|
+
dataSpace = V1_deserializeDataSpace(entity.content);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
else {
|
|
661
|
+
const entity = await this.baseStore.depotServerClient.getVersionEntity(coordinates.group_id, coordinates.artifact_id, coordinates.version, coordinates.data_product);
|
|
662
|
+
dataSpace = V1_deserializeDataSpace(entity.content);
|
|
663
|
+
}
|
|
664
|
+
if (dataSpace && dataSpace.executionContexts.length > 0) {
|
|
665
|
+
const defaultCtxName = dataSpace.defaultExecutionContext;
|
|
666
|
+
const execCtx = dataSpace.executionContexts.find((c) => c.name === defaultCtxName) ??
|
|
667
|
+
guaranteeNonNullable(dataSpace.executionContexts[0]);
|
|
668
|
+
const ctx = new QueryExplicitExecutionContextInfo();
|
|
669
|
+
ctx.mapping = execCtx.mapping.path;
|
|
670
|
+
ctx.runtime = execCtx.defaultRuntime.path;
|
|
671
|
+
runInAction(() => {
|
|
672
|
+
this.pureExecutionContext = ctx;
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
catch (error) {
|
|
677
|
+
assertErrorThrown(error);
|
|
678
|
+
addThinkingStep(setMessages, `Warning: Could not resolve execution context — ${error.message}`);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
*askFollowUp(text) {
|
|
682
|
+
const trimmed = text.trim();
|
|
683
|
+
if (!trimmed ||
|
|
684
|
+
this.isSending ||
|
|
685
|
+
!this.plugin ||
|
|
686
|
+
!this.selectedProductCoordinates) {
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
this.isSending = true;
|
|
690
|
+
this.questionText = '';
|
|
691
|
+
this.messages = [...this.messages, ...createMessagePair(trimmed)];
|
|
692
|
+
const setMessages = this.createMessageSetter();
|
|
693
|
+
try {
|
|
694
|
+
this.stage = MarketplaceAIChatStage.QUERYING;
|
|
695
|
+
const relevantDatasets = (yield this.enrichWithEntitySearch(trimmed, setMessages));
|
|
696
|
+
yield this.dispatchWithSql2(trimmed, relevantDatasets, setMessages);
|
|
697
|
+
this.stage = MarketplaceAIChatStage.RESULTS;
|
|
698
|
+
}
|
|
699
|
+
catch (error) {
|
|
700
|
+
assertErrorThrown(error);
|
|
701
|
+
finishWithThinkingError(setMessages, error.message, Date.now(), classifyError(error));
|
|
702
|
+
}
|
|
703
|
+
finally {
|
|
704
|
+
this.isSending = false;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
async enrichWithEntitySearch(question, setMessages) {
|
|
708
|
+
const coordinates = this.selectedProductCoordinates;
|
|
709
|
+
if (!coordinates) {
|
|
710
|
+
return [];
|
|
711
|
+
}
|
|
712
|
+
addThinkingStep(setMessages, 'Searching for relevant datasets and fields...');
|
|
713
|
+
try {
|
|
714
|
+
const env = this.baseStore.envState.lakehouseEnvironment;
|
|
715
|
+
const entitySearchOptions = {
|
|
716
|
+
groupId: coordinates.group_id,
|
|
717
|
+
artifactId: coordinates.artifact_id,
|
|
718
|
+
versionId: coordinates.version,
|
|
719
|
+
path: coordinates.data_product,
|
|
720
|
+
...(this.selectedDataProductId === undefined
|
|
721
|
+
? {}
|
|
722
|
+
: { dataProductId: this.selectedDataProductId }),
|
|
723
|
+
searchType: FieldSearchType.HYBRID,
|
|
724
|
+
pageSize: DATASET_SEARCH_PAGE_SIZE,
|
|
725
|
+
};
|
|
726
|
+
const [primaryRaw, diversityRaw] = await Promise.all([
|
|
727
|
+
this.baseStore.marketplaceServerClient.entitySearch(env, question, entitySearchOptions),
|
|
728
|
+
this.baseStore.marketplaceServerClient
|
|
729
|
+
.entitySearch(env, coordinates.data_product.split('::').pop() ?? 'data', entitySearchOptions)
|
|
730
|
+
.catch(() => undefined),
|
|
731
|
+
]);
|
|
732
|
+
const primaryResponse = EntitySearchResponse.serialization.fromJson(primaryRaw);
|
|
733
|
+
const results = primaryResponse.results;
|
|
734
|
+
this.mergeDiversityResults(results, diversityRaw);
|
|
735
|
+
if (results.length > 0) {
|
|
736
|
+
const topDataset = guaranteeNonNullable(results[0]);
|
|
737
|
+
addThinkingStep(setMessages, `Found ${results.length} relevant dataset${results.length > 1 ? 's' : ''} — top: ${topDataset.datasetName}`);
|
|
738
|
+
if (this.selectedProductMetadata) {
|
|
739
|
+
const datasetSummaries = results
|
|
740
|
+
.slice(0, MAX_RELEVANT_SERVICES)
|
|
741
|
+
.map((r) => ({
|
|
742
|
+
title: r.datasetName,
|
|
743
|
+
...(r.datasetDescription === undefined
|
|
744
|
+
? {}
|
|
745
|
+
: { description: r.datasetDescription }),
|
|
746
|
+
}));
|
|
747
|
+
const existingTitles = new Set(this.selectedProductMetadata.serviceSummaries.map((s) => s.title));
|
|
748
|
+
const newSummaries = datasetSummaries.filter((s) => !existingTitles.has(s.title));
|
|
749
|
+
const currentMetadata = this.selectedProductMetadata;
|
|
750
|
+
runInAction(() => {
|
|
751
|
+
this.selectedProductMetadata = {
|
|
752
|
+
...currentMetadata,
|
|
753
|
+
serviceSummaries: [
|
|
754
|
+
...currentMetadata.serviceSummaries,
|
|
755
|
+
...newSummaries,
|
|
756
|
+
],
|
|
757
|
+
};
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
this.buildServicesFromEntitySearch(results, setMessages);
|
|
761
|
+
await this.resolveEntityCandidates(question, results, coordinates);
|
|
762
|
+
}
|
|
763
|
+
return results.map((r) => r.datasetName);
|
|
764
|
+
}
|
|
765
|
+
catch (error) {
|
|
766
|
+
assertErrorThrown(error);
|
|
767
|
+
addThinkingStep(setMessages, `Warning: Dataset search unavailable — ${error.message}`);
|
|
768
|
+
return [];
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
async resolveEntityCandidates(question, results, coordinates) {
|
|
772
|
+
const entitiesWithPaths = results.filter((r) => r.datasetDetails?.modelPath);
|
|
773
|
+
if (entitiesWithPaths.length === 0 || !this.plugin) {
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
const candidates = entitiesWithPaths.map((r) => ({
|
|
777
|
+
datasetName: r.datasetName,
|
|
778
|
+
...(r.datasetDescription === undefined
|
|
779
|
+
? {}
|
|
780
|
+
: { description: r.datasetDescription }),
|
|
781
|
+
modelPath: guaranteeNonNullable(r.datasetDetails).modelPath,
|
|
782
|
+
similarityScore: r.similarityScore,
|
|
783
|
+
}));
|
|
784
|
+
runInAction(() => {
|
|
785
|
+
this.lastEntityCandidates = candidates
|
|
786
|
+
.slice(0, MAX_PRODUCT_SUGGESTIONS)
|
|
787
|
+
.map((c) => ({
|
|
788
|
+
datasetName: c.datasetName,
|
|
789
|
+
modelPath: c.modelPath,
|
|
790
|
+
...(c.description === undefined
|
|
791
|
+
? {}
|
|
792
|
+
: { description: c.description }),
|
|
793
|
+
}));
|
|
794
|
+
});
|
|
795
|
+
try {
|
|
796
|
+
const resolved = await this.plugin.disambiguateEntity(question, candidates, this.config, this.pureExecutionContext, coordinates);
|
|
797
|
+
runInAction(() => {
|
|
798
|
+
this.lastResolvedEntities = resolved;
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
catch {
|
|
802
|
+
const topEntity = entitiesWithPaths[0];
|
|
803
|
+
if (topEntity) {
|
|
804
|
+
const resolved = new LegendAIResolvedEntities();
|
|
805
|
+
resolved.rootEntity =
|
|
806
|
+
topEntity.datasetDetails?.modelPath ?? topEntity.datasetName;
|
|
807
|
+
resolved.relatedEntities = entitiesWithPaths
|
|
808
|
+
.slice(1, MAX_RELEVANT_SERVICES + 1)
|
|
809
|
+
.map((r) => r.datasetDetails?.modelPath)
|
|
810
|
+
.filter((p) => p !== undefined);
|
|
811
|
+
runInAction(() => {
|
|
812
|
+
this.lastResolvedEntities = resolved;
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
mergeDiversityResults(results, diversityRaw) {
|
|
818
|
+
if (!diversityRaw) {
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
const diversityResponse = EntitySearchResponse.serialization.fromJson(diversityRaw);
|
|
822
|
+
const existingPaths = new Set(results
|
|
823
|
+
.filter((r) => r.datasetDetails?.modelPath)
|
|
824
|
+
.map((r) => guaranteeNonNullable(r.datasetDetails).modelPath));
|
|
825
|
+
for (const r of diversityResponse.results) {
|
|
826
|
+
if (r.datasetDetails?.modelPath &&
|
|
827
|
+
!existingPaths.has(r.datasetDetails.modelPath)) {
|
|
828
|
+
results.push(r);
|
|
829
|
+
existingPaths.add(r.datasetDetails.modelPath);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
buildServicesFromEntitySearch(results, setMessages) {
|
|
834
|
+
const coordinates = this.selectedProductCoordinates;
|
|
835
|
+
if (!coordinates) {
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
// Skip service building for legacy dataspaces so the flow routes to the orchestrator instead.
|
|
839
|
+
const firstResult = results[0];
|
|
840
|
+
if (firstResult?.dataProductDetails?._type === DataProductDetailsType.LEGACY) {
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
const fallbackPath = coordinates.data_product;
|
|
844
|
+
const services = [];
|
|
845
|
+
let totalColumns = 0;
|
|
846
|
+
for (const result of results) {
|
|
847
|
+
const fields = result.relatedFields ?? [];
|
|
848
|
+
const columns = fields.map((f) => ({
|
|
849
|
+
name: f.fieldName,
|
|
850
|
+
type: f.fieldType ?? 'String',
|
|
851
|
+
...(f.fieldDescription === undefined
|
|
852
|
+
? {}
|
|
853
|
+
: { documentation: f.fieldDescription }),
|
|
854
|
+
}));
|
|
855
|
+
totalColumns += columns.length;
|
|
856
|
+
services.push({
|
|
857
|
+
title: result.datasetName,
|
|
858
|
+
pattern: `/${result.datasetName}`,
|
|
859
|
+
columns,
|
|
860
|
+
parameters: [],
|
|
861
|
+
sourceType: TDSServiceSourceType.ACCESS_POINT,
|
|
862
|
+
dataProductPath: result.datasetDetails?.modelPath ?? fallbackPath,
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
if (services.length > 0) {
|
|
866
|
+
runInAction(() => {
|
|
867
|
+
this.resolvedProductServices = services;
|
|
868
|
+
});
|
|
869
|
+
addThinkingStep(setMessages, `Loaded ${services.length} relevant dataset${services.length > 1 ? 's' : ''} with ${totalColumns} fields`);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
getServicesForQuery(relevantDatasetNames) {
|
|
873
|
+
if (this.resolvedProductServices.length === 0) {
|
|
874
|
+
return [];
|
|
875
|
+
}
|
|
876
|
+
if (relevantDatasetNames.length === 0) {
|
|
877
|
+
return this.resolvedProductServices.slice(0, MAX_RELEVANT_SERVICES);
|
|
878
|
+
}
|
|
879
|
+
const relevantSet = new Set(relevantDatasetNames.map((n) => n.toLowerCase()));
|
|
880
|
+
const relevant = [];
|
|
881
|
+
for (const service of this.resolvedProductServices) {
|
|
882
|
+
if (relevantSet.has(service.title.toLowerCase())) {
|
|
883
|
+
relevant.push(service);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
relevant.sort((a, b) => {
|
|
887
|
+
const aIdx = relevantDatasetNames.findIndex((n) => n.toLowerCase() === a.title.toLowerCase());
|
|
888
|
+
const bIdx = relevantDatasetNames.findIndex((n) => n.toLowerCase() === b.title.toLowerCase());
|
|
889
|
+
return aIdx - bIdx;
|
|
890
|
+
});
|
|
891
|
+
return relevant;
|
|
892
|
+
}
|
|
893
|
+
async dispatchWithSql2(question, relevantDatasetNames, setMessages) {
|
|
894
|
+
const plugin = this.plugin;
|
|
895
|
+
const coordinates = this.selectedProductCoordinates;
|
|
896
|
+
const metadata = this.selectedProductMetadata;
|
|
897
|
+
if (!plugin || !coordinates || !metadata) {
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
const config = this.config;
|
|
901
|
+
const history = this.buildConversationHistory();
|
|
902
|
+
const context = { config, plugin, history, setMessages };
|
|
903
|
+
const intent = await plugin.classifyQuestionIntent(question, false, config);
|
|
904
|
+
if (intent === LegendAIQuestionIntent.METADATA) {
|
|
905
|
+
await handleMetadataQuestion(question, metadata, context, Date.now(), true);
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
const services = this.getServicesForQuery(relevantDatasetNames);
|
|
909
|
+
const coordinatesStr = `${coordinates.group_id}:${coordinates.artifact_id}:${coordinates.version}`;
|
|
910
|
+
const startTime = Date.now();
|
|
911
|
+
const contextPromise = this.buildContextPromise(question, metadata, setMessages);
|
|
912
|
+
if (services.length === 0) {
|
|
913
|
+
addThinkingStep(setMessages, 'No dataset schemas available — entity search did not return results for this data product.');
|
|
914
|
+
completeThinkingSteps(setMessages);
|
|
915
|
+
updateLastAssistant(setMessages, () => ({
|
|
916
|
+
textAnswer: 'Could not resolve dataset schemas for this data product. You can try the Legend AI Orchestrator to generate a Pure query instead.',
|
|
917
|
+
isProcessing: false,
|
|
918
|
+
}));
|
|
919
|
+
this.offerOrchestratorFallback(question, setMessages, startTime);
|
|
920
|
+
await contextPromise;
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
const totalColumns = services.reduce((sum, s) => sum + s.columns.length, 0);
|
|
924
|
+
addThinkingStep(setMessages, `Generating Alloy SQL 2.0 query with ${services.length} relevant dataset${services.length > 1 ? 's' : ''} (${totalColumns} columns)...`);
|
|
925
|
+
try {
|
|
926
|
+
const judgedSql = await generateAndJudgeSql(question, services, coordinatesStr, context, startTime);
|
|
927
|
+
if (!judgedSql) {
|
|
928
|
+
this.offerOrchestratorFallback(question, setMessages, startTime);
|
|
929
|
+
await contextPromise;
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
const sqlGenTime = elapsedSeconds(startTime, 2);
|
|
933
|
+
completeThinkingSteps(setMessages);
|
|
934
|
+
updateLastAssistant(setMessages, () => ({
|
|
935
|
+
sql: judgedSql,
|
|
936
|
+
sqlGenTime,
|
|
937
|
+
isExecuting: true,
|
|
938
|
+
}));
|
|
939
|
+
const sqlResult = await executeSqlAndReport(judgedSql, services, config, plugin, setMessages, startTime, coordinates);
|
|
940
|
+
if (!sqlResult) {
|
|
941
|
+
this.offerOrchestratorFallback(question, setMessages, startTime);
|
|
942
|
+
await contextPromise;
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
if (sqlResult.rows.length === 0) {
|
|
946
|
+
const corrected = await this.attemptZeroRowCorrection(judgedSql, question, services, coordinatesStr, setMessages, coordinates);
|
|
947
|
+
if (corrected) {
|
|
948
|
+
await contextPromise;
|
|
949
|
+
await this.safeAnalyzeResults(question, corrected.sql, corrected.result, metadata, context, startTime);
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
const datasetList = services
|
|
953
|
+
.slice(0, MAX_RELEVANT_SERVICES)
|
|
954
|
+
.map((s) => s.title)
|
|
955
|
+
.join(', ');
|
|
956
|
+
const datasetSuffix = services.length > MAX_RELEVANT_SERVICES
|
|
957
|
+
? ` and ${services.length - MAX_RELEVANT_SERVICES} more`
|
|
958
|
+
: '';
|
|
959
|
+
updateLastAssistant(setMessages, () => ({
|
|
960
|
+
textAnswer: `The SQL 2.0 query executed successfully but returned **0 rows**. The applied filters may not match any records in the available datasets, or the specific values may not exist.\n\n**Queried datasets:** ${datasetList}${datasetSuffix}`,
|
|
961
|
+
}));
|
|
962
|
+
this.offerOrchestratorFallback(question, setMessages, startTime);
|
|
963
|
+
await contextPromise;
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
await contextPromise;
|
|
967
|
+
await this.safeAnalyzeResults(question, judgedSql, sqlResult, metadata, context, startTime);
|
|
968
|
+
}
|
|
969
|
+
catch (error) {
|
|
970
|
+
assertErrorThrown(error);
|
|
971
|
+
addThinkingStep(setMessages, `SQL 2.0 failed: ${error.message}`);
|
|
972
|
+
const datasetContext = services.length > 0
|
|
973
|
+
? `\n\nAvailable datasets: ${services.map((s) => s.title).join(', ')}`
|
|
974
|
+
: '';
|
|
975
|
+
finishWithThinkingError(setMessages, `Alloy SQL 2.0 encountered an error: ${error.message}${datasetContext}`, startTime, classifyError(error));
|
|
976
|
+
this.offerOrchestratorFallback(question, setMessages, startTime);
|
|
977
|
+
await contextPromise;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
async safeAnalyzeResults(question, sql, result, metadata, context, startTime) {
|
|
981
|
+
try {
|
|
982
|
+
await analyzeOrchestratorResults(question, sql, result, metadata, context, startTime);
|
|
983
|
+
}
|
|
984
|
+
catch {
|
|
985
|
+
completeThinkingSteps(context.setMessages);
|
|
986
|
+
updateLastAssistant(context.setMessages, () => ({
|
|
987
|
+
isProcessing: false,
|
|
988
|
+
thinkingDuration: elapsedSeconds(startTime),
|
|
989
|
+
}));
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
async attemptZeroRowCorrection(currentSql, question, services, coordinatesStr, setMessages, dataProductCoordinates) {
|
|
993
|
+
const config = this.config;
|
|
994
|
+
const plugin = this.plugin;
|
|
995
|
+
if (!plugin) {
|
|
996
|
+
return undefined;
|
|
997
|
+
}
|
|
998
|
+
addThinkingStep(setMessages, 'Query returned 0 rows, attempting filter correction...');
|
|
999
|
+
const prompt = plugin.buildZeroRowCorrectionPrompt(currentSql, question, services, coordinatesStr);
|
|
1000
|
+
if (!prompt) {
|
|
1001
|
+
return undefined;
|
|
1002
|
+
}
|
|
1003
|
+
try {
|
|
1004
|
+
const raw = await plugin.callLLM(prompt, config);
|
|
1005
|
+
const trimmed = raw
|
|
1006
|
+
.trim()
|
|
1007
|
+
.replace(/^```\w*\n?/, '')
|
|
1008
|
+
.replace(/\n?```$/, '')
|
|
1009
|
+
.replace(/;\s*$/, '')
|
|
1010
|
+
.trim();
|
|
1011
|
+
if (trimmed.length === 0 ||
|
|
1012
|
+
!trimmed.toLowerCase().startsWith('select') ||
|
|
1013
|
+
trimmed === currentSql) {
|
|
1014
|
+
return undefined;
|
|
1015
|
+
}
|
|
1016
|
+
addThinkingStep(setMessages, 'Retrying with corrected filters...');
|
|
1017
|
+
updateLastAssistant(setMessages, () => ({ sql: trimmed }));
|
|
1018
|
+
const retryResult = await plugin.executeLakehouseSql(trimmed, dataProductCoordinates, config);
|
|
1019
|
+
if (retryResult.rows.length > 0) {
|
|
1020
|
+
const sqlGenTime = elapsedSeconds(Date.now(), 2);
|
|
1021
|
+
completeThinkingSteps(setMessages);
|
|
1022
|
+
updateLastAssistant(setMessages, () => ({
|
|
1023
|
+
sql: trimmed,
|
|
1024
|
+
sqlGenTime,
|
|
1025
|
+
isExecuting: false,
|
|
1026
|
+
}));
|
|
1027
|
+
return { sql: trimmed, result: retryResult };
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
catch {
|
|
1031
|
+
/* empty */
|
|
1032
|
+
}
|
|
1033
|
+
return undefined;
|
|
1034
|
+
}
|
|
1035
|
+
offerOrchestratorFallback(question, setMessages, startTime) {
|
|
1036
|
+
this.pendingFallbackQuestion = question;
|
|
1037
|
+
completeThinkingSteps(setMessages);
|
|
1038
|
+
updateLastAssistant(setMessages, () => ({
|
|
1039
|
+
fallbackAction: {
|
|
1040
|
+
label: 'Ask Legend AI Orchestrator to generate Pure query',
|
|
1041
|
+
actionId: LEGEND_AI_ORCHESTRATOR_FALLBACK_ACTION_ID,
|
|
1042
|
+
},
|
|
1043
|
+
isProcessing: false,
|
|
1044
|
+
thinkingDuration: elapsedSeconds(startTime),
|
|
1045
|
+
}));
|
|
1046
|
+
}
|
|
1047
|
+
*runOrchestratorFallback(messageId) {
|
|
1048
|
+
const question = this.pendingFallbackQuestion;
|
|
1049
|
+
const plugin = this.plugin;
|
|
1050
|
+
const coordinates = this.selectedProductCoordinates;
|
|
1051
|
+
const metadata = this.selectedProductMetadata;
|
|
1052
|
+
if (!question || !plugin || !coordinates || !metadata) {
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
this.isSending = true;
|
|
1056
|
+
const setMessages = this.createMessageSetter();
|
|
1057
|
+
setMessages((prev) => prev.map((m) => m.id === messageId && m.role === LegendAIMessageRole.ASSISTANT
|
|
1058
|
+
? {
|
|
1059
|
+
...m,
|
|
1060
|
+
fallbackAction: null,
|
|
1061
|
+
error: null,
|
|
1062
|
+
isProcessing: true,
|
|
1063
|
+
}
|
|
1064
|
+
: m));
|
|
1065
|
+
try {
|
|
1066
|
+
this.stage = MarketplaceAIChatStage.QUERYING;
|
|
1067
|
+
const history = this.buildConversationHistory();
|
|
1068
|
+
const context = {
|
|
1069
|
+
config: this.config,
|
|
1070
|
+
plugin,
|
|
1071
|
+
history,
|
|
1072
|
+
setMessages,
|
|
1073
|
+
};
|
|
1074
|
+
addThinkingStep(setMessages, 'Switching to Legend AI Orchestrator...');
|
|
1075
|
+
if (this.lastEntityCandidates.length > 0) {
|
|
1076
|
+
const numbered = this.lastEntityCandidates
|
|
1077
|
+
.map((c, i) => `${i + 1}. ${c.modelPath}`)
|
|
1078
|
+
.join(' ');
|
|
1079
|
+
addThinkingStep(setMessages, `Found potential root entity classes: ${numbered}`);
|
|
1080
|
+
const defaultEntity = this.lastResolvedEntities?.rootEntity ??
|
|
1081
|
+
this.lastEntityCandidates[0]?.modelPath;
|
|
1082
|
+
if (defaultEntity) {
|
|
1083
|
+
addThinkingStep(setMessages, `Picking ${defaultEntity} as root entity to generate Pure query`);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
if (!this.pureExecutionContext) {
|
|
1087
|
+
yield this.resolveExecutionContext(setMessages);
|
|
1088
|
+
}
|
|
1089
|
+
const contextPromise = this.buildContextPromise(question, metadata, setMessages);
|
|
1090
|
+
yield Promise.all([
|
|
1091
|
+
processQuestionViaOrchestrator(question, coordinates, metadata, context, this.pureExecutionContext, this.lastResolvedEntities),
|
|
1092
|
+
contextPromise,
|
|
1093
|
+
]);
|
|
1094
|
+
this.stage = MarketplaceAIChatStage.RESULTS;
|
|
1095
|
+
this.pendingFallbackQuestion = undefined;
|
|
1096
|
+
}
|
|
1097
|
+
catch (error) {
|
|
1098
|
+
assertErrorThrown(error);
|
|
1099
|
+
finishWithThinkingError(setMessages, error.message, Date.now(), classifyError(error));
|
|
1100
|
+
}
|
|
1101
|
+
finally {
|
|
1102
|
+
this.isSending = false;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
//# sourceMappingURL=LegendMarketplaceAIChatStore.js.map
|