nttp 1.0.0
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/README.md +245 -0
- package/dist/NTTP.d.ts +121 -0
- package/dist/NTTP.d.ts.map +1 -0
- package/dist/NTTP.js +229 -0
- package/dist/NTTP.js.map +1 -0
- package/dist/cache/exact-cache.d.ts +46 -0
- package/dist/cache/exact-cache.d.ts.map +1 -0
- package/dist/cache/exact-cache.js +104 -0
- package/dist/cache/exact-cache.js.map +1 -0
- package/dist/cache/index.d.ts +8 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +7 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/semantic-cache.d.ts +82 -0
- package/dist/cache/semantic-cache.d.ts.map +1 -0
- package/dist/cache/semantic-cache.js +243 -0
- package/dist/cache/semantic-cache.js.map +1 -0
- package/dist/cache/types.d.ts +135 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/cache/types.js +5 -0
- package/dist/cache/types.js.map +1 -0
- package/dist/cache.d.ts +71 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +129 -0
- package/dist/cache.js.map +1 -0
- package/dist/errors.d.ts +35 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +55 -0
- package/dist/errors.js.map +1 -0
- package/dist/executor.d.ts +82 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +395 -0
- package/dist/executor.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/intent.d.ts +55 -0
- package/dist/intent.d.ts.map +1 -0
- package/dist/intent.js +183 -0
- package/dist/intent.js.map +1 -0
- package/dist/llm.d.ts +60 -0
- package/dist/llm.d.ts.map +1 -0
- package/dist/llm.js +171 -0
- package/dist/llm.js.map +1 -0
- package/dist/types.d.ts +280 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +61 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +19 -0
- package/dist/utils.js.map +1 -0
- package/package.json +94 -0
package/dist/executor.js
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query execution, SQL generation, and schema inference.
|
|
3
|
+
* Main orchestration pipeline for NTTP.
|
|
4
|
+
*/
|
|
5
|
+
import { SQLGenerationError, SQLExecutionError, LLMError, } from './errors.js';
|
|
6
|
+
/**
|
|
7
|
+
* JSON Schema for SQL generation (for structured outputs).
|
|
8
|
+
*/
|
|
9
|
+
const SQL_GENERATION_JSON_SCHEMA = {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
sql: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
description: 'The SQL query to execute',
|
|
15
|
+
},
|
|
16
|
+
params: {
|
|
17
|
+
type: 'array',
|
|
18
|
+
items: {
|
|
19
|
+
type: ['string', 'integer', 'number', 'boolean', 'null'],
|
|
20
|
+
},
|
|
21
|
+
description: 'Parameters for the SQL query',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
required: ['sql', 'params'],
|
|
25
|
+
additionalProperties: false,
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* System prompt for SQL generation.
|
|
29
|
+
*/
|
|
30
|
+
const SQL_GENERATION_SYSTEM_PROMPT = `You are an expert SQL generator.
|
|
31
|
+
Generate safe, read-only SQL queries from structured intents.
|
|
32
|
+
|
|
33
|
+
{schema}
|
|
34
|
+
|
|
35
|
+
Rules:
|
|
36
|
+
- Use parameterized queries with ? placeholders for values
|
|
37
|
+
- Add LIMIT clause (max 1000)
|
|
38
|
+
- Only SELECT queries - no UPDATE, DELETE, DROP, ALTER, INSERT
|
|
39
|
+
- Use proper JOINs for relationships between tables
|
|
40
|
+
- Handle filters intelligently (e.g., status='active', created_at > date)
|
|
41
|
+
|
|
42
|
+
Return JSON:
|
|
43
|
+
{
|
|
44
|
+
"sql": "SELECT ... FROM ... WHERE ... LIMIT ?",
|
|
45
|
+
"params": [value1, value2, ...]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
Examples:
|
|
49
|
+
Intent: {"entity": "users", "operation": "list", "filters": {"status": "active"}, "limit": 10}
|
|
50
|
+
Response: {"sql": "SELECT * FROM users WHERE status = ? LIMIT ?", "params": ["active", 10]}
|
|
51
|
+
|
|
52
|
+
Intent: {"entity": "orders", "operation": "count", "filters": {"status": "pending"}}
|
|
53
|
+
Response: {"sql": "SELECT COUNT(*) as count FROM orders WHERE status = ?", "params": ["pending"]}
|
|
54
|
+
`;
|
|
55
|
+
/**
|
|
56
|
+
* Service for executing queries and managing SQL generation.
|
|
57
|
+
*/
|
|
58
|
+
export class QueryExecutor {
|
|
59
|
+
db;
|
|
60
|
+
llm;
|
|
61
|
+
cache;
|
|
62
|
+
l1Cache;
|
|
63
|
+
l2Cache;
|
|
64
|
+
defaultLimit = 100;
|
|
65
|
+
constructor(db, llm, cache, l1Cache, l2Cache) {
|
|
66
|
+
this.db = db;
|
|
67
|
+
this.llm = llm;
|
|
68
|
+
this.cache = cache;
|
|
69
|
+
this.l1Cache = l1Cache;
|
|
70
|
+
this.l2Cache = l2Cache;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Execute query with v2 3-layer caching.
|
|
74
|
+
*/
|
|
75
|
+
async execute(options) {
|
|
76
|
+
const startTime = Date.now();
|
|
77
|
+
const { query, intent, useCache, forceNewSchema } = options;
|
|
78
|
+
// If v2 not enabled, fall back to v1 behavior
|
|
79
|
+
if (!this.l1Cache && !this.l2Cache) {
|
|
80
|
+
return this.executeV1(options);
|
|
81
|
+
}
|
|
82
|
+
// ─────────────────────────────────────────────────────────
|
|
83
|
+
// L1: EXACT MATCH (hash-based cache)
|
|
84
|
+
// Cost: $0.00 | Latency: <1ms
|
|
85
|
+
// ─────────────────────────────────────────────────────────
|
|
86
|
+
if (this.l1Cache && useCache && !forceNewSchema) {
|
|
87
|
+
const l1Hit = this.l1Cache.get(query);
|
|
88
|
+
if (l1Hit) {
|
|
89
|
+
const data = await this.executeRaw(l1Hit.sql, l1Hit.params);
|
|
90
|
+
const meta = {
|
|
91
|
+
cacheLayer: 1,
|
|
92
|
+
cost: 0,
|
|
93
|
+
latency: Date.now() - startTime,
|
|
94
|
+
};
|
|
95
|
+
return {
|
|
96
|
+
query,
|
|
97
|
+
data,
|
|
98
|
+
schemaId: l1Hit.schemaId,
|
|
99
|
+
cacheHit: true,
|
|
100
|
+
intent,
|
|
101
|
+
sql: l1Hit.sql,
|
|
102
|
+
params: l1Hit.params,
|
|
103
|
+
meta,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// ─────────────────────────────────────────────────────────
|
|
108
|
+
// L2: SEMANTIC MATCH (embedding-based similarity)
|
|
109
|
+
// Cost: ~$0.0001 | Latency: 50-100ms
|
|
110
|
+
// ─────────────────────────────────────────────────────────
|
|
111
|
+
let l2Embedding;
|
|
112
|
+
if (this.l2Cache && useCache && !forceNewSchema) {
|
|
113
|
+
const { match, embedding } = await this.l2Cache.find(query);
|
|
114
|
+
l2Embedding = embedding; // Save for L3 if needed
|
|
115
|
+
if (match) {
|
|
116
|
+
const data = await this.executeRaw(match.result.sql, match.result.params);
|
|
117
|
+
// Promote to L1 for future exact matches
|
|
118
|
+
if (this.l1Cache) {
|
|
119
|
+
this.l1Cache.set(query, match.result);
|
|
120
|
+
}
|
|
121
|
+
const meta = {
|
|
122
|
+
cacheLayer: 2,
|
|
123
|
+
cost: 0.0001,
|
|
124
|
+
latency: Date.now() - startTime,
|
|
125
|
+
similarity: match.similarity,
|
|
126
|
+
};
|
|
127
|
+
return {
|
|
128
|
+
query,
|
|
129
|
+
data,
|
|
130
|
+
schemaId: match.result.schemaId,
|
|
131
|
+
cacheHit: true,
|
|
132
|
+
intent,
|
|
133
|
+
sql: match.result.sql,
|
|
134
|
+
params: match.result.params,
|
|
135
|
+
meta,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
// L2 miss - embedding saved for reuse in L3 to prevent double API billing
|
|
139
|
+
}
|
|
140
|
+
// ─────────────────────────────────────────────────────────
|
|
141
|
+
// L3: LLM FALLBACK (full pipeline)
|
|
142
|
+
// Cost: ~$0.01 | Latency: 2-3s
|
|
143
|
+
// ─────────────────────────────────────────────────────────
|
|
144
|
+
const schemaId = this.generateSchemaId(intent);
|
|
145
|
+
const { sql, params } = await this.generateSql(intent);
|
|
146
|
+
const data = await this.executeRaw(sql, params);
|
|
147
|
+
const result = {
|
|
148
|
+
schemaId,
|
|
149
|
+
sql,
|
|
150
|
+
params,
|
|
151
|
+
hitCount: 1,
|
|
152
|
+
createdAt: new Date(),
|
|
153
|
+
lastUsedAt: new Date(),
|
|
154
|
+
};
|
|
155
|
+
// Populate both L1 and L2 caches
|
|
156
|
+
if (this.l1Cache) {
|
|
157
|
+
this.l1Cache.set(query, result);
|
|
158
|
+
}
|
|
159
|
+
if (this.l2Cache) {
|
|
160
|
+
// Reuse embedding from L2 miss to prevent double API billing
|
|
161
|
+
if (l2Embedding) {
|
|
162
|
+
this.l2Cache.addWithEmbedding(query, l2Embedding, result);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
// L2 not checked (cache disabled or forced new schema)
|
|
166
|
+
await this.l2Cache.add(query, result);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Also populate v1 schema cache (for backward compat)
|
|
170
|
+
const resultSchema = this.inferSchemaFromResults(data);
|
|
171
|
+
const schemaDefinition = {
|
|
172
|
+
schema_id: schemaId,
|
|
173
|
+
intent_pattern: intent.normalized_text,
|
|
174
|
+
generated_sql: sql,
|
|
175
|
+
sql_params: params,
|
|
176
|
+
result_schema: resultSchema,
|
|
177
|
+
use_count: 1,
|
|
178
|
+
created_at: new Date(),
|
|
179
|
+
last_used_at: new Date(),
|
|
180
|
+
example_queries: [query],
|
|
181
|
+
pinned: false,
|
|
182
|
+
};
|
|
183
|
+
await this.cache.set(schemaId, schemaDefinition);
|
|
184
|
+
const meta = {
|
|
185
|
+
cacheLayer: 3,
|
|
186
|
+
cost: 0.01,
|
|
187
|
+
latency: Date.now() - startTime,
|
|
188
|
+
};
|
|
189
|
+
return {
|
|
190
|
+
query,
|
|
191
|
+
data,
|
|
192
|
+
schemaId,
|
|
193
|
+
cacheHit: false,
|
|
194
|
+
intent,
|
|
195
|
+
sql,
|
|
196
|
+
params,
|
|
197
|
+
meta,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Execute query with v1 caching (legacy).
|
|
202
|
+
*/
|
|
203
|
+
async executeV1(options) {
|
|
204
|
+
const { query, intent, useCache, forceNewSchema } = options;
|
|
205
|
+
// Generate schema ID from intent
|
|
206
|
+
const schemaId = this.generateSchemaId(intent);
|
|
207
|
+
// Check cache if enabled and not forcing new schema
|
|
208
|
+
if (useCache && !forceNewSchema) {
|
|
209
|
+
const cachedSchema = await this.cache.get(schemaId);
|
|
210
|
+
if (cachedSchema) {
|
|
211
|
+
// Cache hit - use cached SQL
|
|
212
|
+
const data = await this.executeRaw(cachedSchema.generated_sql, cachedSchema.sql_params);
|
|
213
|
+
// Update cache usage
|
|
214
|
+
await this.cache.updateUsage(schemaId);
|
|
215
|
+
await this.cache.addExampleQuery(schemaId, query);
|
|
216
|
+
return {
|
|
217
|
+
query,
|
|
218
|
+
data,
|
|
219
|
+
schemaId,
|
|
220
|
+
cacheHit: true,
|
|
221
|
+
intent,
|
|
222
|
+
sql: cachedSchema.generated_sql,
|
|
223
|
+
params: cachedSchema.sql_params,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// Cache miss - generate and execute SQL
|
|
228
|
+
const { sql, params } = await this.generateSql(intent);
|
|
229
|
+
const data = await this.executeRaw(sql, params);
|
|
230
|
+
// Infer schema from results
|
|
231
|
+
const resultSchema = this.inferSchemaFromResults(data);
|
|
232
|
+
// Store in cache
|
|
233
|
+
const schemaDefinition = {
|
|
234
|
+
schema_id: schemaId,
|
|
235
|
+
intent_pattern: intent.normalized_text,
|
|
236
|
+
generated_sql: sql,
|
|
237
|
+
sql_params: params,
|
|
238
|
+
result_schema: resultSchema,
|
|
239
|
+
use_count: 1,
|
|
240
|
+
created_at: new Date(),
|
|
241
|
+
last_used_at: new Date(),
|
|
242
|
+
example_queries: [query],
|
|
243
|
+
pinned: false,
|
|
244
|
+
};
|
|
245
|
+
await this.cache.set(schemaId, schemaDefinition);
|
|
246
|
+
return {
|
|
247
|
+
query,
|
|
248
|
+
data,
|
|
249
|
+
schemaId,
|
|
250
|
+
cacheHit: false,
|
|
251
|
+
intent,
|
|
252
|
+
sql,
|
|
253
|
+
params,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Generate SQL query from intent.
|
|
258
|
+
*/
|
|
259
|
+
async generateSql(intent) {
|
|
260
|
+
try {
|
|
261
|
+
// Prepare intent for LLM
|
|
262
|
+
const intentDict = {
|
|
263
|
+
entity: intent.entity,
|
|
264
|
+
operation: intent.operation,
|
|
265
|
+
filters: intent.filters,
|
|
266
|
+
limit: intent.limit || this.defaultLimit,
|
|
267
|
+
fields: intent.fields,
|
|
268
|
+
sort: intent.sort,
|
|
269
|
+
};
|
|
270
|
+
const schemaDescription = await this.getSchemaDescription();
|
|
271
|
+
const systemPrompt = SQL_GENERATION_SYSTEM_PROMPT.replace('{schema}', schemaDescription);
|
|
272
|
+
// Call LLM to generate SQL
|
|
273
|
+
const result = await this.llm.callStructured(`Generate SQL for this intent:\n${JSON.stringify(intentDict, null, 2)}`, systemPrompt, SQL_GENERATION_JSON_SCHEMA, 0.0);
|
|
274
|
+
// Validate SQL safety
|
|
275
|
+
this.validateSqlSafety(result.sql);
|
|
276
|
+
return result;
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
if (error instanceof LLMError) {
|
|
280
|
+
throw new SQLGenerationError(`Failed to generate SQL: ${error}`);
|
|
281
|
+
}
|
|
282
|
+
throw new SQLGenerationError(`SQL generation failed: ${error}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Execute raw SQL query.
|
|
287
|
+
*/
|
|
288
|
+
async executeRaw(sql, params = []) {
|
|
289
|
+
try {
|
|
290
|
+
const result = await this.db.raw(sql, params);
|
|
291
|
+
// Normalize different dialect return formats
|
|
292
|
+
if (Array.isArray(result)) {
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
295
|
+
if (result.rows)
|
|
296
|
+
return result.rows; // PostgreSQL
|
|
297
|
+
if (Array.isArray(result[0]))
|
|
298
|
+
return result[0]; // MySQL
|
|
299
|
+
if (result.recordset)
|
|
300
|
+
return result.recordset; // SQL Server
|
|
301
|
+
return [];
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
throw new SQLExecutionError(`Query failed: ${error}`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Validate SQL safety (read-only).
|
|
309
|
+
*/
|
|
310
|
+
validateSqlSafety(sql) {
|
|
311
|
+
const dangerous = [
|
|
312
|
+
/\bUPDATE\b/i,
|
|
313
|
+
/\bDELETE\b/i,
|
|
314
|
+
/\bDROP\b/i,
|
|
315
|
+
/\bALTER\b/i,
|
|
316
|
+
/\bINSERT\b/i,
|
|
317
|
+
/\bCREATE\b/i,
|
|
318
|
+
];
|
|
319
|
+
for (const pattern of dangerous) {
|
|
320
|
+
if (pattern.test(sql)) {
|
|
321
|
+
throw new SQLGenerationError(`Dangerous SQL operation detected: ${pattern.source}`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (!sql.trim().match(/^(SELECT|WITH)\b/i)) {
|
|
325
|
+
throw new SQLGenerationError('SQL must start with SELECT or WITH');
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Infer JSON schema from query results.
|
|
330
|
+
*/
|
|
331
|
+
inferSchemaFromResults(results) {
|
|
332
|
+
if (results.length === 0) {
|
|
333
|
+
return {};
|
|
334
|
+
}
|
|
335
|
+
const schema = {};
|
|
336
|
+
const sample = results[0];
|
|
337
|
+
for (const [key, value] of Object.entries(sample)) {
|
|
338
|
+
schema[key] = { type: this.inferFieldType(value) };
|
|
339
|
+
}
|
|
340
|
+
return schema;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Infer type of a field value.
|
|
344
|
+
*/
|
|
345
|
+
inferFieldType(value) {
|
|
346
|
+
if (value === null)
|
|
347
|
+
return 'null';
|
|
348
|
+
if (typeof value === 'boolean')
|
|
349
|
+
return 'boolean';
|
|
350
|
+
if (typeof value === 'number') {
|
|
351
|
+
return Number.isInteger(value) ? 'integer' : 'number';
|
|
352
|
+
}
|
|
353
|
+
if (typeof value === 'string') {
|
|
354
|
+
// Detect dates
|
|
355
|
+
if (/^\d{4}-\d{2}-\d{2}/.test(value)) {
|
|
356
|
+
return 'string'; // Keep as string, but could be 'date'
|
|
357
|
+
}
|
|
358
|
+
return 'string';
|
|
359
|
+
}
|
|
360
|
+
if (Array.isArray(value))
|
|
361
|
+
return 'array';
|
|
362
|
+
if (typeof value === 'object')
|
|
363
|
+
return 'object';
|
|
364
|
+
return 'string';
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Get schema description for LLM.
|
|
368
|
+
*/
|
|
369
|
+
async getSchemaDescription() {
|
|
370
|
+
const tables = await this.db
|
|
371
|
+
.select('name')
|
|
372
|
+
.from('sqlite_master')
|
|
373
|
+
.where('type', 'table')
|
|
374
|
+
.andWhere('name', 'not like', 'sqlite_%');
|
|
375
|
+
const lines = ['Database schema:'];
|
|
376
|
+
for (const { name } of tables) {
|
|
377
|
+
const columns = await this.db(name).columnInfo();
|
|
378
|
+
const columnNames = Object.keys(columns).join(', ');
|
|
379
|
+
lines.push(`- ${name} (${columnNames})`);
|
|
380
|
+
}
|
|
381
|
+
return lines.join('\n');
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Generate schema ID from intent (SHA256 hash).
|
|
385
|
+
*/
|
|
386
|
+
generateSchemaId(intent) {
|
|
387
|
+
const crypto = require('crypto');
|
|
388
|
+
const hash = crypto
|
|
389
|
+
.createHash('sha256')
|
|
390
|
+
.update(intent.normalized_text)
|
|
391
|
+
.digest('hex');
|
|
392
|
+
return hash.substring(0, 16);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
//# sourceMappingURL=executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NTTP - Natural Text Transfer Protocol
|
|
3
|
+
* Query databases with natural language using Claude AI
|
|
4
|
+
*/
|
|
5
|
+
export { NTTP } from './NTTP.js';
|
|
6
|
+
export type { NTTPConfig, QueryOptions, QueryResult, Intent, SchemaDefinition, CacheStats, } from './types.js';
|
|
7
|
+
export { IntentParseError, SQLGenerationError, SQLExecutionError, LLMError, CacheError, } from './errors.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,YAAY,EACV,UAAU,EACV,YAAY,EACZ,WAAW,EACX,MAAM,EACN,gBAAgB,EAChB,UAAU,GACX,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,QAAQ,EACR,UAAU,GACX,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NTTP - Natural Text Transfer Protocol
|
|
3
|
+
* Query databases with natural language using Claude AI
|
|
4
|
+
*/
|
|
5
|
+
export { NTTP } from './NTTP.js';
|
|
6
|
+
export { IntentParseError, SQLGenerationError, SQLExecutionError, LLMError, CacheError, } from './errors.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AASjC,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,QAAQ,EACR,UAAU,GACX,MAAM,aAAa,CAAC"}
|
package/dist/intent.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intent parsing and normalization for cache key generation.
|
|
3
|
+
*/
|
|
4
|
+
import type { Intent, OperationType } from './types.js';
|
|
5
|
+
import type { LLMService } from './llm.js';
|
|
6
|
+
import type { FilterConditions, SortSpec } from './utils.js';
|
|
7
|
+
/**
|
|
8
|
+
* Raw intent response from LLM before normalization.
|
|
9
|
+
*/
|
|
10
|
+
interface RawIntent {
|
|
11
|
+
entity: string;
|
|
12
|
+
operation: OperationType;
|
|
13
|
+
filters?: FilterConditions;
|
|
14
|
+
limit?: number | null;
|
|
15
|
+
fields?: string[] | null;
|
|
16
|
+
sort?: SortSpec | null;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Service for parsing natural language into structured intents.
|
|
20
|
+
*/
|
|
21
|
+
export declare class IntentParser {
|
|
22
|
+
private llm;
|
|
23
|
+
constructor(llm: LLMService);
|
|
24
|
+
/**
|
|
25
|
+
* Parse natural language query into structured intent.
|
|
26
|
+
*
|
|
27
|
+
* @param query Natural language query
|
|
28
|
+
* @param schemaDescription Database schema description
|
|
29
|
+
* @returns Structured Intent object
|
|
30
|
+
* @throws IntentParseError if parsing fails
|
|
31
|
+
*/
|
|
32
|
+
parse(query: string, schemaDescription: string): Promise<Intent>;
|
|
33
|
+
/**
|
|
34
|
+
* Normalize intent dictionary to canonical form for cache key generation.
|
|
35
|
+
*
|
|
36
|
+
* CRITICAL: This algorithm must match Python exactly to ensure
|
|
37
|
+
* cache key compatibility between implementations.
|
|
38
|
+
*
|
|
39
|
+
* @param intentData Intent dictionary from LLM
|
|
40
|
+
* @returns Normalized string representation
|
|
41
|
+
*/
|
|
42
|
+
normalizeIntentDict(intentData: RawIntent): string;
|
|
43
|
+
/**
|
|
44
|
+
* Generate unique schema ID from intent.
|
|
45
|
+
*
|
|
46
|
+
* CRITICAL: Must use exact same algorithm as Python (SHA256, first 16 chars)
|
|
47
|
+
* to ensure cache compatibility.
|
|
48
|
+
*
|
|
49
|
+
* @param intent Intent object
|
|
50
|
+
* @returns Schema ID (16-char hash)
|
|
51
|
+
*/
|
|
52
|
+
generateSchemaId(intent: Intent): string;
|
|
53
|
+
}
|
|
54
|
+
export {};
|
|
55
|
+
//# sourceMappingURL=intent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intent.d.ts","sourceRoot":"","sources":["../src/intent.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAExD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAyB,gBAAgB,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAyCpF;;GAEG;AACH,UAAU,SAAS;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,aAAa,CAAC;IACzB,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACzB,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;CACvB;AAkCD;;GAEG;AACH,qBAAa,YAAY;IACX,OAAO,CAAC,GAAG;gBAAH,GAAG,EAAE,UAAU;IAEnC;;;;;;;OAOG;IACG,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA4CtE;;;;;;;;OAQG;IACH,mBAAmB,CAAC,UAAU,EAAE,SAAS,GAAG,MAAM;IAkDlD;;;;;;;;OAQG;IACH,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;CAKzC"}
|
package/dist/intent.js
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intent parsing and normalization for cache key generation.
|
|
3
|
+
*/
|
|
4
|
+
import crypto from 'crypto';
|
|
5
|
+
import { IntentParseError, LLMError } from './errors.js';
|
|
6
|
+
/**
|
|
7
|
+
* JSON Schema for intent parsing (for structured outputs).
|
|
8
|
+
*/
|
|
9
|
+
const INTENT_JSON_SCHEMA = {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
entity: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
description: 'Target table name (users, products, orders, or order_items)',
|
|
15
|
+
},
|
|
16
|
+
operation: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
enum: ['list', 'count', 'aggregate', 'filter'],
|
|
19
|
+
description: 'Operation type',
|
|
20
|
+
},
|
|
21
|
+
filters: {
|
|
22
|
+
type: 'object',
|
|
23
|
+
description: 'Filter conditions as key-value pairs',
|
|
24
|
+
additionalProperties: false,
|
|
25
|
+
},
|
|
26
|
+
limit: {
|
|
27
|
+
type: ['integer', 'null'],
|
|
28
|
+
description: 'Result limit, or null for default',
|
|
29
|
+
},
|
|
30
|
+
fields: {
|
|
31
|
+
type: ['array', 'null'],
|
|
32
|
+
items: { type: 'string' },
|
|
33
|
+
description: 'Specific fields to return, or null for all fields',
|
|
34
|
+
},
|
|
35
|
+
sort: {
|
|
36
|
+
type: ['string', 'null'],
|
|
37
|
+
description: "Sort specification (e.g., 'created_at:desc'), or null",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
required: ['entity', 'operation'],
|
|
41
|
+
additionalProperties: false,
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* System prompt for intent parsing.
|
|
45
|
+
*/
|
|
46
|
+
const INTENT_PARSE_SYSTEM_PROMPT = `You are an expert at parsing natural language database queries.
|
|
47
|
+
Extract structured intent from user queries about an e-commerce database.
|
|
48
|
+
|
|
49
|
+
{schema}
|
|
50
|
+
|
|
51
|
+
Return JSON with this exact structure:
|
|
52
|
+
{
|
|
53
|
+
"entity": "<table_name>",
|
|
54
|
+
"operation": "<list|count|aggregate|filter>",
|
|
55
|
+
"filters": {"<field>": "<value>"},
|
|
56
|
+
"limit": <number or null>,
|
|
57
|
+
"fields": [<field names>] or null,
|
|
58
|
+
"sort": "<field:asc|desc>" or null
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
Rules:
|
|
62
|
+
- entity must be a valid table name from the schema above
|
|
63
|
+
- operation: "list" for SELECT, "count" for COUNT, "aggregate" for SUM/AVG/etc
|
|
64
|
+
- filters: extract conditions (e.g., "active users" -> {"status": "active"})
|
|
65
|
+
- limit: extract limit if specified, otherwise null
|
|
66
|
+
- fields: specific fields requested, or null for all fields
|
|
67
|
+
- sort: sorting specification if mentioned
|
|
68
|
+
|
|
69
|
+
Examples:
|
|
70
|
+
- "get all active users" -> {"entity": "users", "operation": "list", "filters": {"status": "active"}, "limit": null}
|
|
71
|
+
- "show me 10 products" -> {"entity": "products", "operation": "list", "filters": {}, "limit": 10}
|
|
72
|
+
- "count pending orders" -> {"entity": "orders", "operation": "count", "filters": {"status": "pending"}, "limit": null}
|
|
73
|
+
`;
|
|
74
|
+
/**
|
|
75
|
+
* Service for parsing natural language into structured intents.
|
|
76
|
+
*/
|
|
77
|
+
export class IntentParser {
|
|
78
|
+
llm;
|
|
79
|
+
constructor(llm) {
|
|
80
|
+
this.llm = llm;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Parse natural language query into structured intent.
|
|
84
|
+
*
|
|
85
|
+
* @param query Natural language query
|
|
86
|
+
* @param schemaDescription Database schema description
|
|
87
|
+
* @returns Structured Intent object
|
|
88
|
+
* @throws IntentParseError if parsing fails
|
|
89
|
+
*/
|
|
90
|
+
async parse(query, schemaDescription) {
|
|
91
|
+
try {
|
|
92
|
+
// Prepare system prompt with schema
|
|
93
|
+
const systemPrompt = INTENT_PARSE_SYSTEM_PROMPT.replace('{schema}', schemaDescription);
|
|
94
|
+
// Call LLM to parse intent with structured outputs (guaranteed schema compliance)
|
|
95
|
+
const result = await this.llm.callStructured(query, systemPrompt, INTENT_JSON_SCHEMA, 0.0);
|
|
96
|
+
// Ensure filters is a dict (guaranteed by schema, but defensive programming)
|
|
97
|
+
if (!result.filters) {
|
|
98
|
+
result.filters = {};
|
|
99
|
+
}
|
|
100
|
+
// Generate normalized text for cache key
|
|
101
|
+
const normalized = this.normalizeIntentDict(result);
|
|
102
|
+
// Create Intent object
|
|
103
|
+
const intent = {
|
|
104
|
+
entity: result.entity,
|
|
105
|
+
operation: result.operation,
|
|
106
|
+
filters: result.filters || {},
|
|
107
|
+
limit: result.limit,
|
|
108
|
+
fields: result.fields,
|
|
109
|
+
sort: result.sort,
|
|
110
|
+
normalized_text: normalized,
|
|
111
|
+
};
|
|
112
|
+
return intent;
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
if (error instanceof LLMError) {
|
|
116
|
+
throw new IntentParseError(`Failed to understand query: ${error}`);
|
|
117
|
+
}
|
|
118
|
+
throw new IntentParseError(`Failed to parse query: ${error}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Normalize intent dictionary to canonical form for cache key generation.
|
|
123
|
+
*
|
|
124
|
+
* CRITICAL: This algorithm must match Python exactly to ensure
|
|
125
|
+
* cache key compatibility between implementations.
|
|
126
|
+
*
|
|
127
|
+
* @param intentData Intent dictionary from LLM
|
|
128
|
+
* @returns Normalized string representation
|
|
129
|
+
*/
|
|
130
|
+
normalizeIntentDict(intentData) {
|
|
131
|
+
// Extract key components
|
|
132
|
+
const entity = (intentData.entity || '').toLowerCase().trim();
|
|
133
|
+
const operation = (intentData.operation || '').toLowerCase().trim();
|
|
134
|
+
const filters = intentData.filters || {};
|
|
135
|
+
const limit = intentData.limit;
|
|
136
|
+
const fields = intentData.fields || [];
|
|
137
|
+
const sort = intentData.sort;
|
|
138
|
+
// Normalize filters: sort keys and convert values to lowercase strings
|
|
139
|
+
const normalizedFilters = {};
|
|
140
|
+
for (const [key, value] of Object.entries(filters)) {
|
|
141
|
+
const keyClean = key.toLowerCase().trim();
|
|
142
|
+
const valueClean = typeof value === 'string' ? value.toLowerCase().trim() : String(value);
|
|
143
|
+
normalizedFilters[keyClean] = valueClean;
|
|
144
|
+
}
|
|
145
|
+
// Sort filter keys for consistency
|
|
146
|
+
const sortedFilters = Object.entries(normalizedFilters).sort(([a], [b]) => a.localeCompare(b));
|
|
147
|
+
// Build normalized representation
|
|
148
|
+
const parts = [`entity:${entity}`, `operation:${operation}`];
|
|
149
|
+
if (sortedFilters.length > 0) {
|
|
150
|
+
const filtersStr = sortedFilters.map(([k, v]) => `${k}=${v}`).join(',');
|
|
151
|
+
parts.push(`filters:${filtersStr}`);
|
|
152
|
+
}
|
|
153
|
+
if (limit) {
|
|
154
|
+
parts.push(`limit:${limit}`);
|
|
155
|
+
}
|
|
156
|
+
if (fields && fields.length > 0) {
|
|
157
|
+
const fieldsStr = fields
|
|
158
|
+
.map((f) => String(f).toLowerCase())
|
|
159
|
+
.sort()
|
|
160
|
+
.join(',');
|
|
161
|
+
parts.push(`fields:${fieldsStr}`);
|
|
162
|
+
}
|
|
163
|
+
if (sort) {
|
|
164
|
+
parts.push(`sort:${String(sort).toLowerCase()}`);
|
|
165
|
+
}
|
|
166
|
+
return parts.join('|');
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Generate unique schema ID from intent.
|
|
170
|
+
*
|
|
171
|
+
* CRITICAL: Must use exact same algorithm as Python (SHA256, first 16 chars)
|
|
172
|
+
* to ensure cache compatibility.
|
|
173
|
+
*
|
|
174
|
+
* @param intent Intent object
|
|
175
|
+
* @returns Schema ID (16-char hash)
|
|
176
|
+
*/
|
|
177
|
+
generateSchemaId(intent) {
|
|
178
|
+
const normalized = intent.normalized_text;
|
|
179
|
+
const hash = crypto.createHash('sha256').update(normalized).digest('hex');
|
|
180
|
+
return hash.substring(0, 16);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=intent.js.map
|