@memberjunction/query-gen 0.0.1 → 2.126.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/CHANGELOG.md +34 -0
  3. package/COORDINATOR.md +768 -0
  4. package/IMPLEMENTATION_PLAN.md +1753 -0
  5. package/LLM_ENTITY_GROUPING_PLAN.md +977 -0
  6. package/README.md +675 -29
  7. package/dist/cli/commands/export.d.ts +15 -0
  8. package/dist/cli/commands/export.d.ts.map +1 -0
  9. package/dist/cli/commands/export.js +178 -0
  10. package/dist/cli/commands/export.js.map +1 -0
  11. package/dist/cli/commands/generate.d.ts +19 -0
  12. package/dist/cli/commands/generate.d.ts.map +1 -0
  13. package/dist/cli/commands/generate.js +282 -0
  14. package/dist/cli/commands/generate.js.map +1 -0
  15. package/dist/cli/commands/validate.d.ts +17 -0
  16. package/dist/cli/commands/validate.d.ts.map +1 -0
  17. package/dist/cli/commands/validate.js +193 -0
  18. package/dist/cli/commands/validate.js.map +1 -0
  19. package/dist/cli/config.d.ts +51 -0
  20. package/dist/cli/config.d.ts.map +1 -0
  21. package/dist/cli/config.js +142 -0
  22. package/dist/cli/config.js.map +1 -0
  23. package/dist/cli/index.d.ts +13 -0
  24. package/dist/cli/index.d.ts.map +1 -0
  25. package/dist/cli/index.js +57 -0
  26. package/dist/cli/index.js.map +1 -0
  27. package/dist/core/EntityGrouper.d.ts +74 -0
  28. package/dist/core/EntityGrouper.d.ts.map +1 -0
  29. package/dist/core/EntityGrouper.js +246 -0
  30. package/dist/core/EntityGrouper.js.map +1 -0
  31. package/dist/core/MetadataExporter.d.ts +59 -0
  32. package/dist/core/MetadataExporter.d.ts.map +1 -0
  33. package/dist/core/MetadataExporter.js +151 -0
  34. package/dist/core/MetadataExporter.js.map +1 -0
  35. package/dist/core/QueryDatabaseWriter.d.ts +50 -0
  36. package/dist/core/QueryDatabaseWriter.d.ts.map +1 -0
  37. package/dist/core/QueryDatabaseWriter.js +152 -0
  38. package/dist/core/QueryDatabaseWriter.js.map +1 -0
  39. package/dist/core/QueryFixer.d.ts +48 -0
  40. package/dist/core/QueryFixer.d.ts.map +1 -0
  41. package/dist/core/QueryFixer.js +115 -0
  42. package/dist/core/QueryFixer.js.map +1 -0
  43. package/dist/core/QueryRefiner.d.ts +94 -0
  44. package/dist/core/QueryRefiner.d.ts.map +1 -0
  45. package/dist/core/QueryRefiner.js +267 -0
  46. package/dist/core/QueryRefiner.js.map +1 -0
  47. package/dist/core/QueryTester.d.ts +70 -0
  48. package/dist/core/QueryTester.d.ts.map +1 -0
  49. package/dist/core/QueryTester.js +243 -0
  50. package/dist/core/QueryTester.js.map +1 -0
  51. package/dist/core/QueryWriter.d.ts +57 -0
  52. package/dist/core/QueryWriter.d.ts.map +1 -0
  53. package/dist/core/QueryWriter.js +184 -0
  54. package/dist/core/QueryWriter.js.map +1 -0
  55. package/dist/core/QuestionGenerator.d.ts +58 -0
  56. package/dist/core/QuestionGenerator.d.ts.map +1 -0
  57. package/dist/core/QuestionGenerator.js +145 -0
  58. package/dist/core/QuestionGenerator.js.map +1 -0
  59. package/dist/data/schema.d.ts +230 -0
  60. package/dist/data/schema.d.ts.map +1 -0
  61. package/dist/data/schema.js +6 -0
  62. package/dist/data/schema.js.map +1 -0
  63. package/dist/index.d.ts +28 -0
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +77 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/prompts/PromptNames.d.ts +32 -0
  68. package/dist/prompts/PromptNames.d.ts.map +1 -0
  69. package/dist/prompts/PromptNames.js +35 -0
  70. package/dist/prompts/PromptNames.js.map +1 -0
  71. package/dist/utils/category-builder.d.ts +28 -0
  72. package/dist/utils/category-builder.d.ts.map +1 -0
  73. package/dist/utils/category-builder.js +90 -0
  74. package/dist/utils/category-builder.js.map +1 -0
  75. package/dist/utils/entity-helpers.d.ts +49 -0
  76. package/dist/utils/entity-helpers.d.ts.map +1 -0
  77. package/dist/utils/entity-helpers.js +189 -0
  78. package/dist/utils/entity-helpers.js.map +1 -0
  79. package/dist/utils/error-handlers.d.ts +19 -0
  80. package/dist/utils/error-handlers.d.ts.map +1 -0
  81. package/dist/utils/error-handlers.js +41 -0
  82. package/dist/utils/error-handlers.js.map +1 -0
  83. package/dist/utils/graph-helpers.d.ts +51 -0
  84. package/dist/utils/graph-helpers.d.ts.map +1 -0
  85. package/dist/utils/graph-helpers.js +82 -0
  86. package/dist/utils/graph-helpers.js.map +1 -0
  87. package/dist/utils/prompt-helpers.d.ts +25 -0
  88. package/dist/utils/prompt-helpers.d.ts.map +1 -0
  89. package/dist/utils/prompt-helpers.js +66 -0
  90. package/dist/utils/prompt-helpers.js.map +1 -0
  91. package/dist/utils/query-helpers.d.ts +23 -0
  92. package/dist/utils/query-helpers.d.ts.map +1 -0
  93. package/dist/utils/query-helpers.js +34 -0
  94. package/dist/utils/query-helpers.js.map +1 -0
  95. package/dist/utils/user-helpers.d.ts +15 -0
  96. package/dist/utils/user-helpers.d.ts.map +1 -0
  97. package/dist/utils/user-helpers.js +32 -0
  98. package/dist/utils/user-helpers.js.map +1 -0
  99. package/dist/vectors/EmbeddingService.d.ts +58 -0
  100. package/dist/vectors/EmbeddingService.d.ts.map +1 -0
  101. package/dist/vectors/EmbeddingService.js +90 -0
  102. package/dist/vectors/EmbeddingService.js.map +1 -0
  103. package/dist/vectors/SimilaritySearch.d.ts +51 -0
  104. package/dist/vectors/SimilaritySearch.d.ts.map +1 -0
  105. package/dist/vectors/SimilaritySearch.js +85 -0
  106. package/dist/vectors/SimilaritySearch.js.map +1 -0
  107. package/docs/API.md +1040 -0
  108. package/docs/ARCHITECTURE.md +1120 -0
  109. package/examples/advanced-usage.ts +401 -0
  110. package/examples/basic-usage.ts +285 -0
  111. package/package.json +48 -6
  112. package/src/cli/commands/export.ts +173 -0
  113. package/src/cli/commands/generate.ts +330 -0
  114. package/src/cli/commands/validate.ts +185 -0
  115. package/src/cli/config.ts +203 -0
  116. package/src/cli/index.ts +63 -0
  117. package/src/core/EntityGrouper.ts +318 -0
  118. package/src/core/MetadataExporter.ts +148 -0
  119. package/src/core/QueryDatabaseWriter.ts +187 -0
  120. package/src/core/QueryFixer.ts +153 -0
  121. package/src/core/QueryRefiner.ts +382 -0
  122. package/src/core/QueryTester.ts +264 -0
  123. package/src/core/QueryWriter.ts +239 -0
  124. package/src/core/QuestionGenerator.ts +199 -0
  125. package/src/data/golden-queries.json +1371 -0
  126. package/src/data/schema.ts +252 -0
  127. package/src/index.ts +49 -0
  128. package/src/prompts/PromptNames.ts +36 -0
  129. package/src/utils/category-builder.ts +97 -0
  130. package/src/utils/entity-helpers.ts +203 -0
  131. package/src/utils/error-handlers.ts +41 -0
  132. package/src/utils/graph-helpers.ts +99 -0
  133. package/src/utils/prompt-helpers.ts +79 -0
  134. package/src/utils/query-helpers.ts +32 -0
  135. package/src/utils/user-helpers.ts +39 -0
  136. package/src/vectors/EmbeddingService.ts +109 -0
  137. package/src/vectors/SimilaritySearch.ts +108 -0
  138. package/tsconfig.json +39 -0
@@ -0,0 +1,153 @@
1
+ /**
2
+ * QueryFixer - Fixes SQL queries that fail execution
3
+ *
4
+ * Uses the SQL Query Fixer AI prompt to analyze errors and generate
5
+ * corrected SQL queries with updated metadata.
6
+ */
7
+
8
+ import { AIEngine } from '@memberjunction/aiengine';
9
+ import { AIPromptEntityExtended } from '@memberjunction/core-entities';
10
+ import { UserInfo, LogStatus } from '@memberjunction/core';
11
+ import { extractErrorMessage } from '../utils/error-handlers';
12
+ import {
13
+ GeneratedQuery,
14
+ EntityMetadataForPrompt,
15
+ BusinessQuestion,
16
+ } from '../data/schema';
17
+ import { PROMPT_SQL_QUERY_FIXER } from '../prompts/PromptNames';
18
+ import { QueryGenConfig } from '../cli/config';
19
+ import { executePromptWithOverrides } from '../utils/prompt-helpers';
20
+
21
+ /**
22
+ * QueryFixer class
23
+ * Fixes SQL queries that fail to execute
24
+ */
25
+ export class QueryFixer {
26
+ constructor(
27
+ private contextUser: UserInfo,
28
+ private config: QueryGenConfig
29
+ ) {}
30
+
31
+ /**
32
+ * Fix a SQL query that failed to execute
33
+ * Uses AI to analyze the error and generate a corrected query
34
+ *
35
+ * @param query - The query that failed
36
+ * @param errorMessage - The error message from execution
37
+ * @param entityMetadata - Entity metadata for context
38
+ * @param businessQuestion - Original business question for context
39
+ * @returns Corrected SQL query with updated metadata
40
+ */
41
+ async fixQuery(
42
+ query: GeneratedQuery,
43
+ errorMessage: string,
44
+ entityMetadata: EntityMetadataForPrompt[],
45
+ businessQuestion: BusinessQuestion
46
+ ): Promise<GeneratedQuery> {
47
+ try {
48
+ // Ensure AIEngine is configured
49
+ const aiEngine = AIEngine.Instance;
50
+ await aiEngine.Config(false, this.contextUser);
51
+
52
+ // Find the SQL Query Fixer prompt
53
+ const prompt = this.findPromptByName(aiEngine, PROMPT_SQL_QUERY_FIXER);
54
+
55
+ // Prepare prompt data
56
+ const promptData = {
57
+ originalSQL: query.sql,
58
+ errorMessage,
59
+ parameters: query.parameters,
60
+ entityMetadata,
61
+ userQuestion: businessQuestion.userQuestion,
62
+ description: businessQuestion.description,
63
+ };
64
+
65
+ // Execute AI prompt to fix the query
66
+ const fixedQuery = await this.executePrompt(prompt, promptData);
67
+
68
+ // Validate the fixed query structure
69
+ this.validateFixedQuery(fixedQuery);
70
+
71
+ return fixedQuery;
72
+ } catch (error: unknown) {
73
+ throw new Error(extractErrorMessage(error, 'QueryFixer.fixQuery'));
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Find prompt by name in AIEngine cache
79
+ * Throws if prompt not found
80
+ */
81
+ private findPromptByName(
82
+ aiEngine: AIEngine,
83
+ promptName: string
84
+ ): AIPromptEntityExtended {
85
+ const prompt = aiEngine.Prompts.find((p) => p.Name === promptName);
86
+ if (!prompt) {
87
+ throw new Error(`Prompt '${promptName}' not found in AIEngine cache`);
88
+ }
89
+ return prompt;
90
+ }
91
+
92
+ /**
93
+ * Execute the SQL Query Fixer AI prompt
94
+ * Parses JSON response and validates structure
95
+ */
96
+ private async executePrompt(
97
+ prompt: AIPromptEntityExtended,
98
+ promptData: {
99
+ originalSQL: string;
100
+ errorMessage: string;
101
+ parameters: GeneratedQuery['parameters'];
102
+ entityMetadata: EntityMetadataForPrompt[];
103
+ userQuestion: string;
104
+ description: string;
105
+ }
106
+ ): Promise<GeneratedQuery> {
107
+ // The SQL Query Fixer template returns { newSQL, reasoning }
108
+ const result = await executePromptWithOverrides<{
109
+ newSQL: string;
110
+ reasoning: string;
111
+ }>(prompt, promptData, this.contextUser, this.config);
112
+
113
+ if (!result || !result.success) {
114
+ throw new Error(
115
+ `AI prompt execution failed: ${result?.errorMessage || 'Unknown error'}`
116
+ );
117
+ }
118
+
119
+ if (!result.result) {
120
+ throw new Error('AI prompt returned no result');
121
+ }
122
+
123
+ // Log the reasoning
124
+ if (this.config.verbose && result.result.reasoning) {
125
+ LogStatus(`Query fix reasoning: ${result.result.reasoning}`);
126
+ }
127
+
128
+ // Return GeneratedQuery format, preserving original parameters
129
+ return {
130
+ sql: result.result.newSQL,
131
+ parameters: promptData.parameters,
132
+ };
133
+ }
134
+
135
+ /**
136
+ * Validate fixed query structure
137
+ * Ensures query has proper metadata
138
+ *
139
+ * @param query - Fixed query to validate
140
+ * @throws Error if query structure is invalid
141
+ */
142
+ private validateFixedQuery(query: GeneratedQuery): void {
143
+ // Validate SQL is present
144
+ if (!query.sql || query.sql.trim().length === 0) {
145
+ throw new Error('Fixed query has empty SQL');
146
+ }
147
+
148
+ // Validate parameters array
149
+ if (!Array.isArray(query.parameters)) {
150
+ throw new Error('Fixed query parameters must be an array');
151
+ }
152
+ }
153
+ }
@@ -0,0 +1,382 @@
1
+ /**
2
+ * QueryRefiner - Iteratively improves queries based on evaluation feedback
3
+ *
4
+ * Uses evaluation and refinement AI prompts to assess if queries answer
5
+ * the business question correctly and improve them through iterations.
6
+ */
7
+
8
+ import { AIEngine } from '@memberjunction/aiengine';
9
+ import { AIPromptEntityExtended } from '@memberjunction/core-entities';
10
+ import { UserInfo, LogStatus } from '@memberjunction/core';
11
+ import { extractErrorMessage } from '../utils/error-handlers';
12
+ import {
13
+ GeneratedQuery,
14
+ BusinessQuestion,
15
+ EntityMetadataForPrompt,
16
+ RefinedQuery,
17
+ QueryEvaluation,
18
+ QueryTestResult,
19
+ } from '../data/schema';
20
+ import { QueryTester } from './QueryTester';
21
+ import { PROMPT_QUERY_EVALUATOR, PROMPT_QUERY_REFINER } from '../prompts/PromptNames';
22
+ import { QueryGenConfig } from '../cli/config';
23
+ import { executePromptWithOverrides } from '../utils/prompt-helpers';
24
+
25
+ /**
26
+ * QueryRefiner class
27
+ * Iteratively refines queries based on evaluation feedback
28
+ */
29
+ export class QueryRefiner {
30
+ private entityMetadata: EntityMetadataForPrompt[] = [];
31
+
32
+ constructor(
33
+ private tester: QueryTester,
34
+ private contextUser: UserInfo,
35
+ private config: QueryGenConfig
36
+ ) {}
37
+
38
+ /**
39
+ * Refine a query through evaluation and improvement iterations
40
+ *
41
+ * Loops up to maxRefinements times, testing and evaluating each iteration.
42
+ * Returns when query passes evaluation or max refinements reached.
43
+ *
44
+ * @param query - Initial generated query
45
+ * @param businessQuestion - Original business question
46
+ * @param entityMetadata - Entity metadata for refinement
47
+ * @param maxRefinements - Maximum refinement iterations (default: 3)
48
+ * @returns Refined query with test results and evaluation
49
+ */
50
+ async refineQuery(
51
+ query: GeneratedQuery,
52
+ businessQuestion: BusinessQuestion,
53
+ entityMetadata: EntityMetadataForPrompt[],
54
+ maxRefinements: number = 3
55
+ ): Promise<RefinedQuery> {
56
+ // Store entity metadata for use in evaluation
57
+ this.entityMetadata = entityMetadata;
58
+
59
+ let currentQuery = query;
60
+ let refinementCount = 0;
61
+
62
+ // Track the last successfully tested query and its results
63
+ let lastWorkingQuery = query;
64
+ let lastWorkingTestResult: QueryTestResult | null = null;
65
+ let lastWorkingEvaluation: QueryEvaluation | null = null;
66
+
67
+ // Ensure AIEngine is configured
68
+ await this.configureAIEngine();
69
+
70
+ while (refinementCount < maxRefinements) {
71
+ // 1. Test the current query
72
+ let testResult: QueryTestResult;
73
+ try {
74
+ testResult = await this.testCurrentQuery(currentQuery);
75
+ // Success! Save this as our last working version
76
+ lastWorkingQuery = currentQuery;
77
+ lastWorkingTestResult = testResult;
78
+ } catch (error: unknown) {
79
+ // Query broke during refinement - revert to last working version
80
+ if (this.config.verbose) {
81
+ LogStatus(`Refinement produced broken query: ${extractErrorMessage(error, 'Refinement Test')}. Reverting to last working version.`);
82
+ }
83
+
84
+ // If we have a previous working version, use that
85
+ if (lastWorkingTestResult && lastWorkingEvaluation) {
86
+ return this.buildSuccessResult(
87
+ lastWorkingQuery,
88
+ lastWorkingTestResult,
89
+ lastWorkingEvaluation,
90
+ refinementCount
91
+ );
92
+ }
93
+
94
+ // No previous working version - throw the error
95
+ throw error;
96
+ }
97
+
98
+ // 2. Evaluate if it answers the question
99
+ const evaluation = await this.evaluateQuery(
100
+ currentQuery,
101
+ businessQuestion,
102
+ testResult
103
+ );
104
+ lastWorkingEvaluation = evaluation;
105
+
106
+ // 3. If evaluation passes, we're done!
107
+ if (this.shouldStopRefining(evaluation)) {
108
+ return this.buildSuccessResult(
109
+ currentQuery,
110
+ testResult,
111
+ evaluation,
112
+ refinementCount
113
+ );
114
+ }
115
+
116
+ // 4. Refine the query based on suggestions
117
+ refinementCount++;
118
+ if (this.config.verbose) {
119
+ LogStatus(`Refinement iteration ${refinementCount}/${maxRefinements}`);
120
+ }
121
+
122
+ currentQuery = await this.performRefinement(
123
+ currentQuery,
124
+ businessQuestion,
125
+ evaluation,
126
+ entityMetadata
127
+ );
128
+ }
129
+
130
+ // Reached max refinements - return best attempt
131
+ // Use the last successfully tested query
132
+ if (lastWorkingTestResult && lastWorkingEvaluation) {
133
+ return {
134
+ query: lastWorkingQuery,
135
+ testResult: lastWorkingTestResult,
136
+ evaluation: lastWorkingEvaluation,
137
+ refinementCount,
138
+ reachedMaxRefinements: true,
139
+ };
140
+ }
141
+
142
+ // Fallback: try to build final result with current query
143
+ return await this.buildFinalResult(
144
+ currentQuery,
145
+ businessQuestion,
146
+ refinementCount
147
+ );
148
+ }
149
+
150
+ /**
151
+ * Configure AIEngine for prompt execution
152
+ * Ensures engine is ready before running prompts
153
+ */
154
+ private async configureAIEngine(): Promise<void> {
155
+ try {
156
+ const aiEngine = AIEngine.Instance;
157
+ await aiEngine.Config(false, this.contextUser);
158
+ } catch (error: unknown) {
159
+ throw new Error(extractErrorMessage(error, 'AIEngine Configuration'));
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Test current query using QueryTester
165
+ * Throws if query testing fails
166
+ */
167
+ private async testCurrentQuery(query: GeneratedQuery): Promise<QueryTestResult> {
168
+ const testResult = await this.tester.testQuery(query);
169
+
170
+ if (!testResult.success) {
171
+ throw new Error(
172
+ `Query testing failed after ${testResult.attempts} attempts: ${testResult.error}`
173
+ );
174
+ }
175
+
176
+ return testResult;
177
+ }
178
+
179
+ /**
180
+ * Determine if refinement should stop based on evaluation
181
+ * Stops if query answers question and doesn't need refinement
182
+ */
183
+ private shouldStopRefining(evaluation: QueryEvaluation): boolean {
184
+ return evaluation.answersQuestion && !evaluation.needsRefinement;
185
+ }
186
+
187
+ /**
188
+ * Build success result when refinement loop completes successfully
189
+ */
190
+ private buildSuccessResult(
191
+ query: GeneratedQuery,
192
+ testResult: QueryTestResult,
193
+ evaluation: QueryEvaluation,
194
+ refinementCount: number
195
+ ): RefinedQuery {
196
+ return {
197
+ query,
198
+ testResult,
199
+ evaluation,
200
+ refinementCount,
201
+ };
202
+ }
203
+
204
+ /**
205
+ * Build final result when max refinements reached
206
+ * Re-tests and re-evaluates final query
207
+ */
208
+ private async buildFinalResult(
209
+ query: GeneratedQuery,
210
+ businessQuestion: BusinessQuestion,
211
+ refinementCount: number
212
+ ): Promise<RefinedQuery> {
213
+ const testResult = await this.tester.testQuery(query);
214
+ const evaluation = await this.evaluateQuery(
215
+ query,
216
+ businessQuestion,
217
+ testResult
218
+ );
219
+
220
+ return {
221
+ query,
222
+ testResult,
223
+ evaluation,
224
+ refinementCount,
225
+ reachedMaxRefinements: true,
226
+ };
227
+ }
228
+
229
+ /**
230
+ * Evaluate if query answers the business question correctly
231
+ * Uses Query Result Evaluator AI prompt
232
+ *
233
+ * @param query - Query to evaluate
234
+ * @param businessQuestion - Original business question
235
+ * @param testResult - Test execution results with sample data
236
+ * @returns Evaluation with confidence and suggestions
237
+ */
238
+ private async evaluateQuery(
239
+ query: GeneratedQuery,
240
+ businessQuestion: BusinessQuestion,
241
+ testResult: QueryTestResult
242
+ ): Promise<QueryEvaluation> {
243
+ try {
244
+ const aiEngine = AIEngine.Instance;
245
+ const prompt = this.findPromptByName(aiEngine, PROMPT_QUERY_EVALUATOR);
246
+
247
+ // Limit sample results to first 10 rows for efficiency
248
+ const sampleResults = testResult.sampleRows?.slice(0, 10) || [];
249
+
250
+ const promptData = {
251
+ userQuestion: businessQuestion.userQuestion,
252
+ description: businessQuestion.description,
253
+ technicalDescription: businessQuestion.technicalDescription,
254
+ entityMetadata: this.entityMetadata,
255
+ generatedSQL: query.sql,
256
+ parameters: query.parameters,
257
+ sampleResults,
258
+ };
259
+
260
+ const evaluation = await this.executePrompt<QueryEvaluation>(
261
+ prompt,
262
+ promptData
263
+ );
264
+
265
+ this.logEvaluation(evaluation);
266
+ return evaluation;
267
+ } catch (error: unknown) {
268
+ throw new Error(extractErrorMessage(error, 'QueryRefiner.evaluateQuery'));
269
+ }
270
+ }
271
+
272
+ /**
273
+ * Refine query based on evaluation feedback
274
+ * Uses Query Refiner AI prompt
275
+ *
276
+ * @param query - Current query to refine
277
+ * @param businessQuestion - Original business question
278
+ * @param evaluation - Evaluation feedback
279
+ * @param entityMetadata - Entity metadata for refinement
280
+ * @returns Refined query with improvements
281
+ */
282
+ private async performRefinement(
283
+ query: GeneratedQuery,
284
+ businessQuestion: BusinessQuestion,
285
+ evaluation: QueryEvaluation,
286
+ entityMetadata: EntityMetadataForPrompt[]
287
+ ): Promise<GeneratedQuery> {
288
+ try {
289
+ const aiEngine = AIEngine.Instance;
290
+ const prompt = this.findPromptByName(aiEngine, PROMPT_QUERY_REFINER);
291
+
292
+ const promptData = {
293
+ userQuestion: businessQuestion.userQuestion,
294
+ description: businessQuestion.description,
295
+ currentSQL: query.sql,
296
+ evaluationFeedback: evaluation,
297
+ entityMetadata,
298
+ };
299
+
300
+ const refinedQuery = await this.executePrompt<
301
+ GeneratedQuery & { improvementsSummary: string }
302
+ >(prompt, promptData);
303
+
304
+ if (this.config.verbose) {
305
+ LogStatus(`Refinements applied: ${refinedQuery.improvementsSummary}`);
306
+ }
307
+
308
+ return {
309
+ sql: refinedQuery.sql,
310
+ parameters: refinedQuery.parameters,
311
+ };
312
+ } catch (error: unknown) {
313
+ throw new Error(
314
+ extractErrorMessage(error, 'QueryRefiner.performRefinement')
315
+ );
316
+ }
317
+ }
318
+
319
+ /**
320
+ * Find prompt by name in AIEngine cache
321
+ * Throws if prompt not found
322
+ */
323
+ private findPromptByName(
324
+ aiEngine: AIEngine,
325
+ promptName: string
326
+ ): AIPromptEntityExtended {
327
+ const prompt = aiEngine.Prompts.find((p) => p.Name === promptName);
328
+ if (!prompt) {
329
+ throw new Error(`Prompt '${promptName}' not found in AIEngine cache`);
330
+ }
331
+ return prompt;
332
+ }
333
+
334
+ /**
335
+ * Execute AI prompt and parse result
336
+ * Generic method for any prompt type
337
+ */
338
+ private async executePrompt<T>(
339
+ prompt: AIPromptEntityExtended,
340
+ promptData: Record<string, unknown>
341
+ ): Promise<T> {
342
+ const result = await executePromptWithOverrides<T>(
343
+ prompt,
344
+ promptData,
345
+ this.contextUser,
346
+ this.config
347
+ );
348
+
349
+ if (!result || !result.success) {
350
+ throw new Error(
351
+ `AI prompt execution failed: ${result?.errorMessage || 'Unknown error'}`
352
+ );
353
+ }
354
+
355
+ if (!result.result) {
356
+ throw new Error('AI prompt returned no result');
357
+ }
358
+
359
+ return result.result;
360
+ }
361
+
362
+ /**
363
+ * Log evaluation results for debugging
364
+ */
365
+ private logEvaluation(evaluation: QueryEvaluation): void {
366
+ if (!this.config.verbose) return;
367
+
368
+ LogStatus(
369
+ `Evaluation: answersQuestion=${evaluation.answersQuestion}, ` +
370
+ `confidence=${evaluation.confidence}, ` +
371
+ `needsRefinement=${evaluation.needsRefinement}`
372
+ );
373
+
374
+ if (evaluation.reasoning) {
375
+ LogStatus(`Reasoning: ${evaluation.reasoning}`);
376
+ }
377
+
378
+ if (evaluation.suggestions.length > 0) {
379
+ LogStatus(`Suggestions: ${evaluation.suggestions.join('; ')}`);
380
+ }
381
+ }
382
+ }