@kiyeonjeon21/datacontext 0.2.0 → 0.3.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.
- package/.cursorrules +12 -0
- package/.env.example +8 -0
- package/.github/workflows/ci.yml +21 -1
- package/.github/workflows/publish.yml +21 -1
- package/CHANGELOG.md +41 -0
- package/README.md +247 -239
- package/cursor-mcp-config.json.example +29 -0
- package/datacontext.db +0 -0
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +145 -0
- package/dist/api/server.js.map +1 -1
- package/dist/api/start-server.d.ts +10 -0
- package/dist/api/start-server.d.ts.map +1 -0
- package/dist/api/start-server.js +73 -0
- package/dist/api/start-server.js.map +1 -0
- package/dist/cli/index.js +462 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/context-service.d.ts +72 -0
- package/dist/core/context-service.d.ts.map +1 -1
- package/dist/core/context-service.js +132 -0
- package/dist/core/context-service.js.map +1 -1
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +5 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/llm-service.d.ts +141 -0
- package/dist/core/llm-service.d.ts.map +1 -0
- package/dist/core/llm-service.js +284 -0
- package/dist/core/llm-service.js.map +1 -0
- package/dist/knowledge/store.d.ts +56 -3
- package/dist/knowledge/store.d.ts.map +1 -1
- package/dist/knowledge/store.js +193 -7
- package/dist/knowledge/store.js.map +1 -1
- package/dist/knowledge/types.d.ts +43 -1
- package/dist/knowledge/types.d.ts.map +1 -1
- package/dist/knowledge/types.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +365 -0
- package/dist/mcp/tools.js.map +1 -1
- package/docs/API.md +173 -0
- package/docs/DEMO_SCRIPT.md +210 -0
- package/docs/MCP_TEST_GUIDE.md +414 -0
- package/docs/SYNC_GUIDE.md +242 -0
- package/package.json +4 -1
- package/src/api/server.ts +160 -0
- package/src/api/start-server.ts +78 -0
- package/src/cli/index.ts +534 -0
- package/src/core/context-service.ts +182 -0
- package/src/core/index.ts +7 -0
- package/src/core/llm-service.ts +359 -0
- package/src/knowledge/store.ts +232 -7
- package/src/knowledge/types.ts +45 -1
- package/src/mcp/tools.ts +415 -0
- package/test-glossary.yaml +55 -0
- package/test-mcp.db +0 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* LLM Service Module
|
|
4
|
+
*
|
|
5
|
+
* Provides AI-powered features using Claude API.
|
|
6
|
+
* Used for auto-generating glossary terms, descriptions, and query suggestions.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const llm = createLLMService();
|
|
11
|
+
*
|
|
12
|
+
* // Generate glossary from user's raw terms
|
|
13
|
+
* const terms = await llm.generateGlossary(
|
|
14
|
+
* "활성 사용자, 최근 주문, VIP 고객",
|
|
15
|
+
* schemaContext
|
|
16
|
+
* );
|
|
17
|
+
*
|
|
18
|
+
* // Generate table descriptions
|
|
19
|
+
* const descriptions = await llm.generateTableDescriptions(tableInfo);
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
23
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.LLMService = void 0;
|
|
27
|
+
exports.createLLMService = createLLMService;
|
|
28
|
+
exports.isLLMAvailable = isLLMAvailable;
|
|
29
|
+
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
30
|
+
const types_js_1 = require("../knowledge/types.js");
|
|
31
|
+
/** LLM Service class */
|
|
32
|
+
class LLMService {
|
|
33
|
+
client;
|
|
34
|
+
model;
|
|
35
|
+
maxTokens;
|
|
36
|
+
constructor(config = {}) {
|
|
37
|
+
const apiKey = config.apiKey || process.env.ANTHROPIC_API_KEY;
|
|
38
|
+
if (!apiKey) {
|
|
39
|
+
throw new Error('Anthropic API key not found. Set ANTHROPIC_API_KEY environment variable or pass apiKey in config.');
|
|
40
|
+
}
|
|
41
|
+
this.client = new sdk_1.default({ apiKey });
|
|
42
|
+
this.model = config.model || process.env.ANTHROPIC_MODEL || 'claude-sonnet-4-20250514';
|
|
43
|
+
this.maxTokens = config.maxTokens || 4096;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Generate glossary terms from user's raw input
|
|
47
|
+
*
|
|
48
|
+
* Takes natural language terms and generates structured BusinessTerm entries
|
|
49
|
+
* with SQL expressions and proper categorization.
|
|
50
|
+
*
|
|
51
|
+
* @param rawTerms - User's raw term input (comma-separated, YAML, or natural language)
|
|
52
|
+
* @param context - Database schema context
|
|
53
|
+
* @param schemaHash - Current schema hash for metadata
|
|
54
|
+
* @returns Array of generated BusinessTerm entries
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```typescript
|
|
58
|
+
* const terms = await llm.generateGlossary(
|
|
59
|
+
* "활성 사용자 = status가 1인 사용자\n최근 주문 = 30일 이내 주문",
|
|
60
|
+
* { tables: [{ name: 'users', columns: [...] }] },
|
|
61
|
+
* "abc123"
|
|
62
|
+
* );
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
async generateGlossary(rawTerms, context, schemaHash = '') {
|
|
66
|
+
const systemPrompt = `You are a database context expert. Your job is to analyze user-provided business terms and generate structured glossary entries that can be used to translate natural language queries into accurate SQL.
|
|
67
|
+
|
|
68
|
+
IMPORTANT RULES:
|
|
69
|
+
1. Generate SQL expressions that are syntactically correct for the given schema
|
|
70
|
+
2. Match terms to actual table/column names in the schema
|
|
71
|
+
3. Be precise with data types (e.g., integer status codes, date intervals)
|
|
72
|
+
4. Include synonyms in multiple languages if the term suggests it
|
|
73
|
+
5. Categorize terms appropriately: status, time, money, entity, metric, filter, custom
|
|
74
|
+
|
|
75
|
+
OUTPUT FORMAT: Return a JSON array of term objects. Each object must have:
|
|
76
|
+
- term: The primary term name
|
|
77
|
+
- synonyms: Array of alternative names (include English, Korean if applicable)
|
|
78
|
+
- definition: Clear explanation of what this term means
|
|
79
|
+
- sqlExpression: SQL condition or expression (if applicable)
|
|
80
|
+
- appliesTo: { tables?: string[], columns?: string[] }
|
|
81
|
+
- category: One of: status, time, money, entity, metric, filter, custom
|
|
82
|
+
- examples: Array of example usage in natural language queries
|
|
83
|
+
|
|
84
|
+
Return ONLY the JSON array, no other text.`;
|
|
85
|
+
const schemaInfo = this.formatSchemaContext(context);
|
|
86
|
+
const userPrompt = `DATABASE SCHEMA:
|
|
87
|
+
${schemaInfo}
|
|
88
|
+
|
|
89
|
+
${context.existingTerms?.length ? `EXISTING TERMS (avoid duplicates):
|
|
90
|
+
${context.existingTerms.map(t => `- ${t.term}: ${t.definition}`).join('\n')}
|
|
91
|
+
` : ''}
|
|
92
|
+
|
|
93
|
+
USER'S RAW TERMS TO PROCESS:
|
|
94
|
+
${rawTerms}
|
|
95
|
+
|
|
96
|
+
Generate structured glossary entries for these terms. Match them to the actual schema above.`;
|
|
97
|
+
try {
|
|
98
|
+
const response = await this.client.messages.create({
|
|
99
|
+
model: this.model,
|
|
100
|
+
max_tokens: this.maxTokens,
|
|
101
|
+
messages: [
|
|
102
|
+
{ role: 'user', content: userPrompt }
|
|
103
|
+
],
|
|
104
|
+
system: systemPrompt,
|
|
105
|
+
});
|
|
106
|
+
const content = response.content[0];
|
|
107
|
+
if (content.type !== 'text') {
|
|
108
|
+
throw new Error('Unexpected response type from Claude');
|
|
109
|
+
}
|
|
110
|
+
// Parse JSON response
|
|
111
|
+
const generated = this.parseJsonResponse(content.text);
|
|
112
|
+
// Convert to BusinessTerm with metadata
|
|
113
|
+
return generated.map(term => ({
|
|
114
|
+
...(0, types_js_1.createKnowledgeMeta)('auto', schemaHash),
|
|
115
|
+
type: 'business_term',
|
|
116
|
+
term: term.term,
|
|
117
|
+
synonyms: term.synonyms || [],
|
|
118
|
+
definition: term.definition,
|
|
119
|
+
sqlExpression: term.sqlExpression,
|
|
120
|
+
appliesTo: term.appliesTo,
|
|
121
|
+
category: term.category,
|
|
122
|
+
examples: term.examples,
|
|
123
|
+
isActive: true,
|
|
124
|
+
}));
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
throw new Error(`Failed to generate glossary: ${error instanceof Error ? error.message : String(error)}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Generate table/column descriptions from schema
|
|
132
|
+
*
|
|
133
|
+
* Analyzes table and column names to generate meaningful descriptions.
|
|
134
|
+
* Useful for cold-start when no documentation exists.
|
|
135
|
+
*
|
|
136
|
+
* @param tableInfo - Table schema information
|
|
137
|
+
* @param schemaHash - Current schema hash
|
|
138
|
+
* @returns Generated TableDescription
|
|
139
|
+
*/
|
|
140
|
+
async generateTableDescription(tableInfo, schemaHash = '') {
|
|
141
|
+
const systemPrompt = `You are a database documentation expert. Analyze the table structure and generate clear, useful descriptions.
|
|
142
|
+
|
|
143
|
+
OUTPUT FORMAT: Return a JSON object with:
|
|
144
|
+
- description: One sentence describing the table's purpose
|
|
145
|
+
- purpose: Detailed explanation of the table's role
|
|
146
|
+
- columns: Array of { name: string, description: string, valueMappings?: Record<string, string> }
|
|
147
|
+
- tags: Array of relevant tags
|
|
148
|
+
|
|
149
|
+
Return ONLY the JSON object, no other text.`;
|
|
150
|
+
const sampleDataStr = tableInfo.sampleData
|
|
151
|
+
? `\n\nSAMPLE DATA:\n${JSON.stringify(tableInfo.sampleData.slice(0, 3), null, 2)}`
|
|
152
|
+
: '';
|
|
153
|
+
const userPrompt = `TABLE: ${tableInfo.name}
|
|
154
|
+
|
|
155
|
+
COLUMNS:
|
|
156
|
+
${tableInfo.columns.map(c => `- ${c.name} (${c.type}${c.nullable ? ', nullable' : ''})`).join('\n')}
|
|
157
|
+
${sampleDataStr}
|
|
158
|
+
|
|
159
|
+
Generate descriptions for this table and its columns.`;
|
|
160
|
+
try {
|
|
161
|
+
const response = await this.client.messages.create({
|
|
162
|
+
model: this.model,
|
|
163
|
+
max_tokens: this.maxTokens,
|
|
164
|
+
messages: [
|
|
165
|
+
{ role: 'user', content: userPrompt }
|
|
166
|
+
],
|
|
167
|
+
system: systemPrompt,
|
|
168
|
+
});
|
|
169
|
+
const content = response.content[0];
|
|
170
|
+
if (content.type !== 'text') {
|
|
171
|
+
throw new Error('Unexpected response type from Claude');
|
|
172
|
+
}
|
|
173
|
+
return this.parseJsonResponse(content.text);
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
throw new Error(`Failed to generate table description: ${error instanceof Error ? error.message : String(error)}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Enhance a user query with glossary context
|
|
181
|
+
*
|
|
182
|
+
* Takes a natural language query and returns an enhanced version
|
|
183
|
+
* with term definitions resolved.
|
|
184
|
+
*
|
|
185
|
+
* @param query - User's natural language query
|
|
186
|
+
* @param terms - Available business terms
|
|
187
|
+
* @returns Enhanced query with context
|
|
188
|
+
*/
|
|
189
|
+
async enhanceQueryWithGlossary(query, terms) {
|
|
190
|
+
if (terms.length === 0) {
|
|
191
|
+
return { enhancedQuery: query, usedTerms: [], suggestedConditions: [] };
|
|
192
|
+
}
|
|
193
|
+
const systemPrompt = `You are a query enhancement assistant. Your job is to identify business terms in user queries and suggest SQL conditions based on the glossary.
|
|
194
|
+
|
|
195
|
+
OUTPUT FORMAT: Return a JSON object with:
|
|
196
|
+
- enhancedQuery: The query with term definitions inline
|
|
197
|
+
- usedTerms: Array of term names that were found in the query
|
|
198
|
+
- suggestedConditions: Array of SQL conditions to apply
|
|
199
|
+
|
|
200
|
+
Return ONLY the JSON object, no other text.`;
|
|
201
|
+
const glossaryStr = terms
|
|
202
|
+
.filter(t => t.isActive)
|
|
203
|
+
.map(t => `- "${t.term}" (${t.synonyms.join(', ')}): ${t.definition}${t.sqlExpression ? ` → SQL: ${t.sqlExpression}` : ''}`)
|
|
204
|
+
.join('\n');
|
|
205
|
+
const userPrompt = `GLOSSARY:
|
|
206
|
+
${glossaryStr}
|
|
207
|
+
|
|
208
|
+
USER QUERY:
|
|
209
|
+
${query}
|
|
210
|
+
|
|
211
|
+
Identify any terms from the glossary used in this query and suggest SQL conditions.`;
|
|
212
|
+
try {
|
|
213
|
+
const response = await this.client.messages.create({
|
|
214
|
+
model: this.model,
|
|
215
|
+
max_tokens: 1024,
|
|
216
|
+
messages: [
|
|
217
|
+
{ role: 'user', content: userPrompt }
|
|
218
|
+
],
|
|
219
|
+
system: systemPrompt,
|
|
220
|
+
});
|
|
221
|
+
const content = response.content[0];
|
|
222
|
+
if (content.type !== 'text') {
|
|
223
|
+
return { enhancedQuery: query, usedTerms: [], suggestedConditions: [] };
|
|
224
|
+
}
|
|
225
|
+
return this.parseJsonResponse(content.text);
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
return { enhancedQuery: query, usedTerms: [], suggestedConditions: [] };
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Parse JSON response from Claude, handling markdown code blocks
|
|
233
|
+
*/
|
|
234
|
+
parseJsonResponse(text) {
|
|
235
|
+
// Remove markdown code blocks if present
|
|
236
|
+
let cleaned = text.trim();
|
|
237
|
+
if (cleaned.startsWith('```json')) {
|
|
238
|
+
cleaned = cleaned.slice(7);
|
|
239
|
+
}
|
|
240
|
+
else if (cleaned.startsWith('```')) {
|
|
241
|
+
cleaned = cleaned.slice(3);
|
|
242
|
+
}
|
|
243
|
+
if (cleaned.endsWith('```')) {
|
|
244
|
+
cleaned = cleaned.slice(0, -3);
|
|
245
|
+
}
|
|
246
|
+
cleaned = cleaned.trim();
|
|
247
|
+
try {
|
|
248
|
+
return JSON.parse(cleaned);
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
throw new Error(`Failed to parse JSON response: ${text.slice(0, 200)}...`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Format schema context for prompts
|
|
256
|
+
*/
|
|
257
|
+
formatSchemaContext(context) {
|
|
258
|
+
return context.tables
|
|
259
|
+
.map(table => {
|
|
260
|
+
const cols = table.columns
|
|
261
|
+
.map(c => ` - ${c.name} (${c.type}${c.nullable ? ', nullable' : ''})`)
|
|
262
|
+
.join('\n');
|
|
263
|
+
return `Table: ${table.name}\n${cols}`;
|
|
264
|
+
})
|
|
265
|
+
.join('\n\n');
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
exports.LLMService = LLMService;
|
|
269
|
+
/**
|
|
270
|
+
* Create an LLM service instance
|
|
271
|
+
*
|
|
272
|
+
* @param config - Service configuration
|
|
273
|
+
* @returns LLMService instance
|
|
274
|
+
*/
|
|
275
|
+
function createLLMService(config = {}) {
|
|
276
|
+
return new LLMService(config);
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Check if LLM service is available (API key configured)
|
|
280
|
+
*/
|
|
281
|
+
function isLLMAvailable() {
|
|
282
|
+
return !!process.env.ANTHROPIC_API_KEY;
|
|
283
|
+
}
|
|
284
|
+
//# sourceMappingURL=llm-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-service.js","sourceRoot":"","sources":["../../src/core/llm-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Knowledge Store
|
|
3
3
|
* Local JSON-based storage for table descriptions, query examples, and business rules
|
|
4
4
|
*/
|
|
5
|
-
import type { TableDescription, QueryExample, BusinessRule, KnowledgeEntry, ColumnDescription } from './types.js';
|
|
5
|
+
import type { TableDescription, QueryExample, BusinessRule, BusinessTerm, KnowledgeEntry, ColumnDescription, TermCategory } from './types.js';
|
|
6
6
|
export declare class KnowledgeStore {
|
|
7
7
|
private dataPath;
|
|
8
8
|
private data;
|
|
@@ -87,14 +87,66 @@ export declare class KnowledgeStore {
|
|
|
87
87
|
priority?: number;
|
|
88
88
|
schemaHash?: string;
|
|
89
89
|
}): Promise<BusinessRule>;
|
|
90
|
+
/**
|
|
91
|
+
* Get all business terms
|
|
92
|
+
*/
|
|
93
|
+
getBusinessTerms(): BusinessTerm[];
|
|
94
|
+
/**
|
|
95
|
+
* Get active business terms
|
|
96
|
+
*/
|
|
97
|
+
getActiveTerms(): BusinessTerm[];
|
|
98
|
+
/**
|
|
99
|
+
* Get business terms for specific tables
|
|
100
|
+
*/
|
|
101
|
+
getTermsForTables(tables: string[]): BusinessTerm[];
|
|
102
|
+
/**
|
|
103
|
+
* Find terms matching a query (by term name or synonyms)
|
|
104
|
+
*/
|
|
105
|
+
findMatchingTerms(query: string): BusinessTerm[];
|
|
106
|
+
/**
|
|
107
|
+
* Add a business term
|
|
108
|
+
*/
|
|
109
|
+
addBusinessTerm(term: string, definition: string, options?: {
|
|
110
|
+
synonyms?: string[];
|
|
111
|
+
sqlExpression?: string;
|
|
112
|
+
appliesTo?: {
|
|
113
|
+
tables?: string[];
|
|
114
|
+
columns?: string[];
|
|
115
|
+
};
|
|
116
|
+
category?: TermCategory;
|
|
117
|
+
examples?: string[];
|
|
118
|
+
schemaHash?: string;
|
|
119
|
+
}): Promise<BusinessTerm>;
|
|
120
|
+
/**
|
|
121
|
+
* Add multiple business terms at once
|
|
122
|
+
*/
|
|
123
|
+
addBusinessTerms(terms: BusinessTerm[]): Promise<BusinessTerm[]>;
|
|
124
|
+
/**
|
|
125
|
+
* Update a business term
|
|
126
|
+
*/
|
|
127
|
+
updateBusinessTerm(id: string, updates: Partial<Omit<BusinessTerm, 'id' | 'type' | 'createdAt'>>): Promise<BusinessTerm>;
|
|
128
|
+
/**
|
|
129
|
+
* Delete a business term
|
|
130
|
+
*/
|
|
131
|
+
deleteBusinessTerm(id: string): Promise<void>;
|
|
90
132
|
/**
|
|
91
133
|
* Get all knowledge entries that may be outdated (schema drift)
|
|
92
134
|
*/
|
|
93
135
|
getOutdatedEntries(currentSchemaHash: string): KnowledgeEntry[];
|
|
94
136
|
/**
|
|
95
137
|
* Build context for AI from knowledge store
|
|
96
|
-
|
|
97
|
-
|
|
138
|
+
*
|
|
139
|
+
* Generates a comprehensive context string including:
|
|
140
|
+
* - Table descriptions and column details
|
|
141
|
+
* - Business glossary/terms with SQL mappings
|
|
142
|
+
* - Business rules and conditions
|
|
143
|
+
* - Query examples
|
|
144
|
+
*
|
|
145
|
+
* @param tables - Tables to include context for
|
|
146
|
+
* @param userQuery - Optional user query to match relevant terms
|
|
147
|
+
* @returns Formatted context string for AI
|
|
148
|
+
*/
|
|
149
|
+
buildContext(tables: string[], userQuery?: string): string;
|
|
98
150
|
private createEmptyData;
|
|
99
151
|
/**
|
|
100
152
|
* Get raw knowledge data (for API/export)
|
|
@@ -111,6 +163,7 @@ export declare class KnowledgeStore {
|
|
|
111
163
|
}>;
|
|
112
164
|
queryExamples: QueryExample[];
|
|
113
165
|
businessRules: BusinessRule[];
|
|
166
|
+
businessTerms: BusinessTerm[];
|
|
114
167
|
};
|
|
115
168
|
}
|
|
116
169
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/knowledge/store.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAEV,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,iBAAiB,
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/knowledge/store.ts"],"names":[],"mappings":"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"}
|
package/dist/knowledge/store.js
CHANGED
|
@@ -236,6 +236,144 @@ class KnowledgeStore {
|
|
|
236
236
|
await this.save();
|
|
237
237
|
return rule;
|
|
238
238
|
}
|
|
239
|
+
// ============ Business Terms (Glossary) ============
|
|
240
|
+
/**
|
|
241
|
+
* Get all business terms
|
|
242
|
+
*/
|
|
243
|
+
getBusinessTerms() {
|
|
244
|
+
return this.data?.businessTerms ?? [];
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Get active business terms
|
|
248
|
+
*/
|
|
249
|
+
getActiveTerms() {
|
|
250
|
+
return this.data?.businessTerms.filter(t => t.isActive) ?? [];
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Get business terms for specific tables
|
|
254
|
+
*/
|
|
255
|
+
getTermsForTables(tables) {
|
|
256
|
+
return this.data?.businessTerms.filter(term => term.isActive &&
|
|
257
|
+
(term.appliesTo?.tables?.some(t => tables.includes(t)) ?? true)) ?? [];
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Find terms matching a query (by term name or synonyms)
|
|
261
|
+
*/
|
|
262
|
+
findMatchingTerms(query) {
|
|
263
|
+
const lowerQuery = query.toLowerCase();
|
|
264
|
+
return this.data?.businessTerms.filter(term => {
|
|
265
|
+
if (!term.isActive)
|
|
266
|
+
return false;
|
|
267
|
+
// Check term name
|
|
268
|
+
if (term.term.toLowerCase().includes(lowerQuery))
|
|
269
|
+
return true;
|
|
270
|
+
if (lowerQuery.includes(term.term.toLowerCase()))
|
|
271
|
+
return true;
|
|
272
|
+
// Check synonyms
|
|
273
|
+
for (const syn of term.synonyms) {
|
|
274
|
+
if (syn.toLowerCase().includes(lowerQuery))
|
|
275
|
+
return true;
|
|
276
|
+
if (lowerQuery.includes(syn.toLowerCase()))
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
return false;
|
|
280
|
+
}) ?? [];
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Add a business term
|
|
284
|
+
*/
|
|
285
|
+
async addBusinessTerm(term, definition, options = {}) {
|
|
286
|
+
if (!this.data) {
|
|
287
|
+
throw new Error('Knowledge store not loaded');
|
|
288
|
+
}
|
|
289
|
+
// Check for duplicates
|
|
290
|
+
const existing = this.data.businessTerms.find(t => t.term.toLowerCase() === term.toLowerCase());
|
|
291
|
+
if (existing) {
|
|
292
|
+
// Update existing term
|
|
293
|
+
return this.updateBusinessTerm(existing.id, {
|
|
294
|
+
definition,
|
|
295
|
+
...options,
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
const schemaHash = options.schemaHash ?? this.data.schemaHash;
|
|
299
|
+
const newTerm = {
|
|
300
|
+
...(0, types_js_1.createKnowledgeMeta)('user', schemaHash),
|
|
301
|
+
type: 'business_term',
|
|
302
|
+
term,
|
|
303
|
+
synonyms: options.synonyms ?? [],
|
|
304
|
+
definition,
|
|
305
|
+
sqlExpression: options.sqlExpression,
|
|
306
|
+
appliesTo: options.appliesTo,
|
|
307
|
+
category: options.category,
|
|
308
|
+
examples: options.examples,
|
|
309
|
+
isActive: true,
|
|
310
|
+
};
|
|
311
|
+
this.data.businessTerms.push(newTerm);
|
|
312
|
+
await this.save();
|
|
313
|
+
return newTerm;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Add multiple business terms at once
|
|
317
|
+
*/
|
|
318
|
+
async addBusinessTerms(terms) {
|
|
319
|
+
if (!this.data) {
|
|
320
|
+
throw new Error('Knowledge store not loaded');
|
|
321
|
+
}
|
|
322
|
+
const added = [];
|
|
323
|
+
for (const term of terms) {
|
|
324
|
+
// Check for duplicates
|
|
325
|
+
const existingIndex = this.data.businessTerms.findIndex(t => t.term.toLowerCase() === term.term.toLowerCase());
|
|
326
|
+
if (existingIndex >= 0) {
|
|
327
|
+
// Update existing
|
|
328
|
+
this.data.businessTerms[existingIndex] = {
|
|
329
|
+
...this.data.businessTerms[existingIndex],
|
|
330
|
+
...term,
|
|
331
|
+
updatedAt: new Date().toISOString(),
|
|
332
|
+
};
|
|
333
|
+
added.push(this.data.businessTerms[existingIndex]);
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
// Add new
|
|
337
|
+
this.data.businessTerms.push(term);
|
|
338
|
+
added.push(term);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
await this.save();
|
|
342
|
+
return added;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Update a business term
|
|
346
|
+
*/
|
|
347
|
+
async updateBusinessTerm(id, updates) {
|
|
348
|
+
if (!this.data) {
|
|
349
|
+
throw new Error('Knowledge store not loaded');
|
|
350
|
+
}
|
|
351
|
+
const index = this.data.businessTerms.findIndex(t => t.id === id);
|
|
352
|
+
if (index < 0) {
|
|
353
|
+
throw new Error(`Business term not found: ${id}`);
|
|
354
|
+
}
|
|
355
|
+
const updated = {
|
|
356
|
+
...this.data.businessTerms[index],
|
|
357
|
+
...updates,
|
|
358
|
+
updatedAt: new Date().toISOString(),
|
|
359
|
+
};
|
|
360
|
+
this.data.businessTerms[index] = updated;
|
|
361
|
+
await this.save();
|
|
362
|
+
return updated;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Delete a business term
|
|
366
|
+
*/
|
|
367
|
+
async deleteBusinessTerm(id) {
|
|
368
|
+
if (!this.data) {
|
|
369
|
+
throw new Error('Knowledge store not loaded');
|
|
370
|
+
}
|
|
371
|
+
const index = this.data.businessTerms.findIndex(t => t.id === id);
|
|
372
|
+
if (index >= 0) {
|
|
373
|
+
this.data.businessTerms.splice(index, 1);
|
|
374
|
+
await this.save();
|
|
375
|
+
}
|
|
376
|
+
}
|
|
239
377
|
// ============ Utilities ============
|
|
240
378
|
/**
|
|
241
379
|
* Get all knowledge entries that may be outdated (schema drift)
|
|
@@ -260,13 +398,55 @@ class KnowledgeStore {
|
|
|
260
398
|
outdated.push(br);
|
|
261
399
|
}
|
|
262
400
|
}
|
|
401
|
+
for (const bt of this.data.businessTerms ?? []) {
|
|
402
|
+
if (bt.schemaHash !== currentSchemaHash) {
|
|
403
|
+
outdated.push(bt);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
263
406
|
return outdated;
|
|
264
407
|
}
|
|
265
408
|
/**
|
|
266
409
|
* Build context for AI from knowledge store
|
|
410
|
+
*
|
|
411
|
+
* Generates a comprehensive context string including:
|
|
412
|
+
* - Table descriptions and column details
|
|
413
|
+
* - Business glossary/terms with SQL mappings
|
|
414
|
+
* - Business rules and conditions
|
|
415
|
+
* - Query examples
|
|
416
|
+
*
|
|
417
|
+
* @param tables - Tables to include context for
|
|
418
|
+
* @param userQuery - Optional user query to match relevant terms
|
|
419
|
+
* @returns Formatted context string for AI
|
|
267
420
|
*/
|
|
268
|
-
buildContext(tables) {
|
|
421
|
+
buildContext(tables, userQuery) {
|
|
269
422
|
const parts = [];
|
|
423
|
+
// Add business glossary FIRST (helps AI understand terminology)
|
|
424
|
+
const terms = userQuery
|
|
425
|
+
? this.findMatchingTerms(userQuery)
|
|
426
|
+
: this.getTermsForTables(tables);
|
|
427
|
+
if (terms.length > 0) {
|
|
428
|
+
parts.push('## Business Glossary');
|
|
429
|
+
parts.push('Use these term definitions when interpreting user requests:');
|
|
430
|
+
parts.push('');
|
|
431
|
+
for (const term of terms) {
|
|
432
|
+
let termInfo = `**${term.term}**`;
|
|
433
|
+
if (term.synonyms.length > 0) {
|
|
434
|
+
termInfo += ` (also: ${term.synonyms.join(', ')})`;
|
|
435
|
+
}
|
|
436
|
+
parts.push(termInfo);
|
|
437
|
+
parts.push(` Definition: ${term.definition}`);
|
|
438
|
+
if (term.sqlExpression) {
|
|
439
|
+
parts.push(` SQL: \`${term.sqlExpression}\``);
|
|
440
|
+
}
|
|
441
|
+
if (term.appliesTo?.tables?.length) {
|
|
442
|
+
parts.push(` Applies to: ${term.appliesTo.tables.join(', ')}`);
|
|
443
|
+
}
|
|
444
|
+
if (term.examples?.length) {
|
|
445
|
+
parts.push(` Example usage: "${term.examples[0]}"`);
|
|
446
|
+
}
|
|
447
|
+
parts.push('');
|
|
448
|
+
}
|
|
449
|
+
}
|
|
270
450
|
// Add table descriptions
|
|
271
451
|
for (const tableName of tables) {
|
|
272
452
|
const desc = this.getTableDescription(tableName);
|
|
@@ -295,10 +475,11 @@ class KnowledgeStore {
|
|
|
295
475
|
const rules = this.getActiveRulesForTables(tables);
|
|
296
476
|
if (rules.length > 0) {
|
|
297
477
|
parts.push('## Business Rules');
|
|
478
|
+
parts.push('Always apply these rules when querying:');
|
|
298
479
|
for (const rule of rules) {
|
|
299
|
-
parts.push(`-
|
|
480
|
+
parts.push(`- **${rule.name}**: ${rule.description}`);
|
|
300
481
|
if (rule.condition) {
|
|
301
|
-
parts.push(`
|
|
482
|
+
parts.push(` SQL condition: \`${rule.condition}\``);
|
|
302
483
|
}
|
|
303
484
|
}
|
|
304
485
|
parts.push('');
|
|
@@ -307,11 +488,14 @@ class KnowledgeStore {
|
|
|
307
488
|
const examples = this.getQueryExamplesForTables(tables);
|
|
308
489
|
if (examples.length > 0) {
|
|
309
490
|
parts.push('## Query Examples');
|
|
491
|
+
parts.push('Reference these verified examples:');
|
|
310
492
|
for (const ex of examples.slice(0, 5)) { // Limit to 5 examples
|
|
311
|
-
parts.push(`Intent: ${ex.intent}`);
|
|
312
|
-
parts.push(
|
|
493
|
+
parts.push(`Intent: "${ex.intent}"`);
|
|
494
|
+
parts.push('```sql');
|
|
495
|
+
parts.push(ex.sql);
|
|
496
|
+
parts.push('```');
|
|
313
497
|
if (ex.explanation) {
|
|
314
|
-
parts.push(`
|
|
498
|
+
parts.push(`Note: ${ex.explanation}`);
|
|
315
499
|
}
|
|
316
500
|
parts.push('');
|
|
317
501
|
}
|
|
@@ -327,6 +511,7 @@ class KnowledgeStore {
|
|
|
327
511
|
tableDescriptions: [],
|
|
328
512
|
queryExamples: [],
|
|
329
513
|
businessRules: [],
|
|
514
|
+
businessTerms: [],
|
|
330
515
|
};
|
|
331
516
|
}
|
|
332
517
|
/**
|
|
@@ -334,7 +519,7 @@ class KnowledgeStore {
|
|
|
334
519
|
*/
|
|
335
520
|
getData() {
|
|
336
521
|
if (!this.data) {
|
|
337
|
-
return { tables: [], queryExamples: [], businessRules: [] };
|
|
522
|
+
return { tables: [], queryExamples: [], businessRules: [], businessTerms: [] };
|
|
338
523
|
}
|
|
339
524
|
return {
|
|
340
525
|
tables: this.data.tableDescriptions.map(td => ({
|
|
@@ -345,6 +530,7 @@ class KnowledgeStore {
|
|
|
345
530
|
})),
|
|
346
531
|
queryExamples: this.data.queryExamples,
|
|
347
532
|
businessRules: this.data.businessRules,
|
|
533
|
+
businessTerms: this.data.businessTerms ?? [],
|
|
348
534
|
};
|
|
349
535
|
}
|
|
350
536
|
}
|