@quantyapp/quanty-mcp-server 1.0.11 → 1.2.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/dist/bin/quanty-mcp-server.exe +0 -0
- package/dist/core/analysisService.d.ts +27 -0
- package/dist/core/analysisService.d.ts.map +1 -1
- package/dist/core/analysisService.js +84 -0
- package/dist/core/bankService.d.ts +26 -1
- package/dist/core/bankService.d.ts.map +1 -1
- package/dist/core/bankService.js +111 -2
- package/dist/core/budgetService.d.ts +190 -0
- package/dist/core/budgetService.d.ts.map +1 -1
- package/dist/core/budgetService.js +606 -1
- package/dist/core/learningService.d.ts +94 -0
- package/dist/core/learningService.d.ts.map +1 -0
- package/dist/core/learningService.js +462 -0
- package/dist/core/matchingService.d.ts +76 -0
- package/dist/core/matchingService.d.ts.map +1 -0
- package/dist/core/matchingService.js +437 -0
- package/dist/mcp/index.js +1180 -189
- package/package.json +10 -1
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { AuthContext } from './auth.js';
|
|
2
|
+
export interface LearningEntry {
|
|
3
|
+
id?: string;
|
|
4
|
+
task_signature: string;
|
|
5
|
+
task_description: string;
|
|
6
|
+
solution_steps: any[];
|
|
7
|
+
context_used?: any;
|
|
8
|
+
success_metrics: {
|
|
9
|
+
execution_time?: number;
|
|
10
|
+
items_processed?: number;
|
|
11
|
+
accuracy?: number;
|
|
12
|
+
};
|
|
13
|
+
confidence_score: number;
|
|
14
|
+
usage_count: number;
|
|
15
|
+
last_used: string;
|
|
16
|
+
created_at: string;
|
|
17
|
+
tenant_id: string;
|
|
18
|
+
}
|
|
19
|
+
export interface OperationPattern {
|
|
20
|
+
id?: string;
|
|
21
|
+
pattern_name: string;
|
|
22
|
+
trigger_keywords: string[];
|
|
23
|
+
preconditions: any;
|
|
24
|
+
action_sequence: any[];
|
|
25
|
+
success_criteria: any;
|
|
26
|
+
variations: any[];
|
|
27
|
+
optimization_hints: string[];
|
|
28
|
+
version: number;
|
|
29
|
+
tenant_id: string;
|
|
30
|
+
}
|
|
31
|
+
export interface KnowledgeContext {
|
|
32
|
+
similar_tasks: LearningEntry[];
|
|
33
|
+
relevant_patterns: OperationPattern[];
|
|
34
|
+
best_practices: string[];
|
|
35
|
+
warnings: string[];
|
|
36
|
+
}
|
|
37
|
+
export declare class QuantyLearningSystem {
|
|
38
|
+
private auth;
|
|
39
|
+
constructor(auth: AuthContext);
|
|
40
|
+
/**
|
|
41
|
+
* Creates a unique signature for a task based on its characteristics
|
|
42
|
+
*/
|
|
43
|
+
private createTaskSignature;
|
|
44
|
+
private hashString;
|
|
45
|
+
/**
|
|
46
|
+
* Load knowledge base from Supabase
|
|
47
|
+
*/
|
|
48
|
+
loadKnowledgeBase(): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Find similar tasks from history
|
|
51
|
+
*/
|
|
52
|
+
findSimilarTasks(task: string, limit?: number): Promise<LearningEntry[]>;
|
|
53
|
+
/**
|
|
54
|
+
* Get relevant knowledge for a task
|
|
55
|
+
*/
|
|
56
|
+
getRelevantKnowledge(task: string): Promise<KnowledgeContext>;
|
|
57
|
+
/**
|
|
58
|
+
* Learn from a successful execution
|
|
59
|
+
*/
|
|
60
|
+
learnFromExecution(task: string, steps: any[], result: any, executionTime: number): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Extract patterns from successful executions
|
|
63
|
+
*/
|
|
64
|
+
private extractAndUpdatePatterns;
|
|
65
|
+
private extractKeywords;
|
|
66
|
+
/**
|
|
67
|
+
* Process user feedback on executions
|
|
68
|
+
*/
|
|
69
|
+
processFeedback(taskSignature: string, feedbackType: 'success' | 'failure' | 'improvement', details?: string): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Get learning statistics
|
|
72
|
+
*/
|
|
73
|
+
getStatistics(): Promise<any>;
|
|
74
|
+
/**
|
|
75
|
+
* Clean up old or low-confidence entries
|
|
76
|
+
*/
|
|
77
|
+
pruneKnowledge(): Promise<void>;
|
|
78
|
+
}
|
|
79
|
+
export declare class ContextBuilder {
|
|
80
|
+
private learning;
|
|
81
|
+
constructor(learning: QuantyLearningSystem);
|
|
82
|
+
/**
|
|
83
|
+
* Build enriched context for a task
|
|
84
|
+
*/
|
|
85
|
+
buildContext(task: string): Promise<string>;
|
|
86
|
+
/**
|
|
87
|
+
* Generate system prompt with accumulated knowledge
|
|
88
|
+
*/
|
|
89
|
+
generateSystemPrompt(basePrompt: string): Promise<string>;
|
|
90
|
+
}
|
|
91
|
+
export declare function initLearningSystem(auth: AuthContext): void;
|
|
92
|
+
export declare function getLearningSystem(): QuantyLearningSystem;
|
|
93
|
+
export declare function getContextBuilder(): ContextBuilder;
|
|
94
|
+
//# sourceMappingURL=learningService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learningService.d.ts","sourceRoot":"","sources":["../../src/core/learningService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
import { supabase } from './supabase.js';
|
|
2
|
+
// ================ CACHE MANAGEMENT ================
|
|
3
|
+
class LearningCache {
|
|
4
|
+
cache = new Map();
|
|
5
|
+
patterns = new Map();
|
|
6
|
+
lastSync = new Date();
|
|
7
|
+
CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
|
8
|
+
isStale() {
|
|
9
|
+
return Date.now() - this.lastSync.getTime() > this.CACHE_TTL;
|
|
10
|
+
}
|
|
11
|
+
clear() {
|
|
12
|
+
this.cache.clear();
|
|
13
|
+
this.patterns.clear();
|
|
14
|
+
}
|
|
15
|
+
addEntry(entry) {
|
|
16
|
+
if (entry.task_signature) {
|
|
17
|
+
this.cache.set(entry.task_signature, entry);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
addPattern(pattern) {
|
|
21
|
+
this.patterns.set(pattern.pattern_name, pattern);
|
|
22
|
+
}
|
|
23
|
+
getEntry(signature) {
|
|
24
|
+
return this.cache.get(signature);
|
|
25
|
+
}
|
|
26
|
+
getPattern(name) {
|
|
27
|
+
return this.patterns.get(name);
|
|
28
|
+
}
|
|
29
|
+
getAllEntries() {
|
|
30
|
+
return Array.from(this.cache.values());
|
|
31
|
+
}
|
|
32
|
+
getAllPatterns() {
|
|
33
|
+
return Array.from(this.patterns.values());
|
|
34
|
+
}
|
|
35
|
+
updateSyncTime() {
|
|
36
|
+
this.lastSync = new Date();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const learningCache = new LearningCache();
|
|
40
|
+
// ================ CORE LEARNING SYSTEM ================
|
|
41
|
+
export class QuantyLearningSystem {
|
|
42
|
+
auth;
|
|
43
|
+
constructor(auth) {
|
|
44
|
+
this.auth = auth;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Creates a unique signature for a task based on its characteristics
|
|
48
|
+
*/
|
|
49
|
+
createTaskSignature(task) {
|
|
50
|
+
// Extract key components from task
|
|
51
|
+
const normalized = task.toLowerCase()
|
|
52
|
+
.replace(/['"]/g, '')
|
|
53
|
+
.replace(/\s+/g, ' ')
|
|
54
|
+
.trim();
|
|
55
|
+
// Extract operation type
|
|
56
|
+
let operationType = 'unknown';
|
|
57
|
+
if (normalized.includes('criar') || normalized.includes('novo'))
|
|
58
|
+
operationType = 'create';
|
|
59
|
+
else if (normalized.includes('adicionar') || normalized.includes('incluir'))
|
|
60
|
+
operationType = 'add';
|
|
61
|
+
else if (normalized.includes('buscar') || normalized.includes('pesquisar'))
|
|
62
|
+
operationType = 'search';
|
|
63
|
+
else if (normalized.includes('analisar') || normalized.includes('análise'))
|
|
64
|
+
operationType = 'analyze';
|
|
65
|
+
else if (normalized.includes('importar'))
|
|
66
|
+
operationType = 'import';
|
|
67
|
+
else if (normalized.includes('excluir') || normalized.includes('remover'))
|
|
68
|
+
operationType = 'delete';
|
|
69
|
+
// Extract entity type
|
|
70
|
+
let entityType = 'unknown';
|
|
71
|
+
if (normalized.includes('orçamento'))
|
|
72
|
+
entityType = 'budget';
|
|
73
|
+
else if (normalized.includes('item') || normalized.includes('insumo'))
|
|
74
|
+
entityType = 'item';
|
|
75
|
+
else if (normalized.includes('composição'))
|
|
76
|
+
entityType = 'composition';
|
|
77
|
+
else if (normalized.includes('banco'))
|
|
78
|
+
entityType = 'bank';
|
|
79
|
+
// Create signature
|
|
80
|
+
return `${operationType}:${entityType}:${this.hashString(normalized)}`;
|
|
81
|
+
}
|
|
82
|
+
hashString(str) {
|
|
83
|
+
let hash = 0;
|
|
84
|
+
for (let i = 0; i < str.length; i++) {
|
|
85
|
+
const char = str.charCodeAt(i);
|
|
86
|
+
hash = ((hash << 5) - hash) + char;
|
|
87
|
+
hash = hash & hash; // Convert to 32-bit integer
|
|
88
|
+
}
|
|
89
|
+
return Math.abs(hash).toString(36);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Load knowledge base from Supabase
|
|
93
|
+
*/
|
|
94
|
+
async loadKnowledgeBase() {
|
|
95
|
+
if (!learningCache.isStale())
|
|
96
|
+
return;
|
|
97
|
+
try {
|
|
98
|
+
// Load learning entries
|
|
99
|
+
const { data: entries, error: entriesError } = await supabase
|
|
100
|
+
.from('ai_learnings')
|
|
101
|
+
.select('*')
|
|
102
|
+
.eq('tenant_id', this.auth.tenantId)
|
|
103
|
+
.order('confidence_score', { ascending: false })
|
|
104
|
+
.limit(100);
|
|
105
|
+
if (!entriesError && entries) {
|
|
106
|
+
learningCache.clear();
|
|
107
|
+
entries.forEach((e) => learningCache.addEntry(e));
|
|
108
|
+
}
|
|
109
|
+
// Load patterns
|
|
110
|
+
const { data: patterns, error: patternsError } = await supabase
|
|
111
|
+
.from('ai_patterns')
|
|
112
|
+
.select('*')
|
|
113
|
+
.eq('tenant_id', this.auth.tenantId)
|
|
114
|
+
.order('version', { ascending: false });
|
|
115
|
+
if (!patternsError && patterns) {
|
|
116
|
+
patterns.forEach((p) => learningCache.addPattern(p));
|
|
117
|
+
}
|
|
118
|
+
learningCache.updateSyncTime();
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
console.error('Failed to load knowledge base:', error);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Find similar tasks from history
|
|
126
|
+
*/
|
|
127
|
+
async findSimilarTasks(task, limit = 5) {
|
|
128
|
+
await this.loadKnowledgeBase();
|
|
129
|
+
const taskLower = task.toLowerCase();
|
|
130
|
+
const taskWords = new Set(taskLower.split(/\s+/));
|
|
131
|
+
const scoredEntries = learningCache.getAllEntries().map(entry => {
|
|
132
|
+
const descWords = new Set(entry.task_description.toLowerCase().split(/\s+/));
|
|
133
|
+
const intersection = new Set([...taskWords].filter(x => descWords.has(x)));
|
|
134
|
+
const union = new Set([...taskWords, ...descWords]);
|
|
135
|
+
// Jaccard similarity
|
|
136
|
+
const similarity = intersection.size / union.size;
|
|
137
|
+
// Boost score based on confidence and usage
|
|
138
|
+
const score = similarity * entry.confidence_score * Math.log(entry.usage_count + 1);
|
|
139
|
+
return { entry, score };
|
|
140
|
+
});
|
|
141
|
+
return scoredEntries
|
|
142
|
+
.sort((a, b) => b.score - a.score)
|
|
143
|
+
.slice(0, limit)
|
|
144
|
+
.map(s => s.entry);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get relevant knowledge for a task
|
|
148
|
+
*/
|
|
149
|
+
async getRelevantKnowledge(task) {
|
|
150
|
+
await this.loadKnowledgeBase();
|
|
151
|
+
const similarTasks = await this.findSimilarTasks(task);
|
|
152
|
+
const taskLower = task.toLowerCase();
|
|
153
|
+
// Find relevant patterns
|
|
154
|
+
const relevantPatterns = learningCache.getAllPatterns().filter(pattern => {
|
|
155
|
+
return pattern.trigger_keywords.some(keyword => taskLower.includes(keyword.toLowerCase()));
|
|
156
|
+
});
|
|
157
|
+
// Extract best practices from successful similar tasks
|
|
158
|
+
const bestPractices = [];
|
|
159
|
+
const warnings = [];
|
|
160
|
+
for (const similar of similarTasks) {
|
|
161
|
+
if (similar.confidence_score > 0.8) {
|
|
162
|
+
if (similar.success_metrics.execution_time && similar.success_metrics.execution_time < 1000) {
|
|
163
|
+
// Fast execution hints could be added here
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// Extract optimization hints from patterns
|
|
168
|
+
for (const pattern of relevantPatterns) {
|
|
169
|
+
if (pattern.optimization_hints) {
|
|
170
|
+
bestPractices.push(...pattern.optimization_hints);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
similar_tasks: similarTasks,
|
|
175
|
+
relevant_patterns: relevantPatterns,
|
|
176
|
+
best_practices: [...new Set(bestPractices)],
|
|
177
|
+
warnings
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Learn from a successful execution
|
|
182
|
+
*/
|
|
183
|
+
async learnFromExecution(task, steps, result, executionTime) {
|
|
184
|
+
const signature = this.createTaskSignature(task);
|
|
185
|
+
// Check if we already have this task
|
|
186
|
+
const existing = learningCache.getEntry(signature);
|
|
187
|
+
if (existing) {
|
|
188
|
+
// Update existing entry
|
|
189
|
+
const updatedEntry = {
|
|
190
|
+
...existing,
|
|
191
|
+
usage_count: existing.usage_count + 1,
|
|
192
|
+
confidence_score: Math.min(1, existing.confidence_score + 0.05),
|
|
193
|
+
last_used: new Date().toISOString(),
|
|
194
|
+
success_metrics: {
|
|
195
|
+
...existing.success_metrics,
|
|
196
|
+
execution_time: (existing.success_metrics.execution_time + executionTime) / 2
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
await supabase
|
|
200
|
+
.from('ai_learnings')
|
|
201
|
+
.update(updatedEntry)
|
|
202
|
+
.eq('id', existing.id);
|
|
203
|
+
learningCache.addEntry(updatedEntry);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
// Create new entry
|
|
207
|
+
const newEntry = {
|
|
208
|
+
task_signature: signature,
|
|
209
|
+
task_description: task,
|
|
210
|
+
solution_steps: steps,
|
|
211
|
+
success_metrics: {
|
|
212
|
+
execution_time: executionTime,
|
|
213
|
+
items_processed: result.items_processed || 0,
|
|
214
|
+
accuracy: 1.0
|
|
215
|
+
},
|
|
216
|
+
confidence_score: 0.7,
|
|
217
|
+
usage_count: 1,
|
|
218
|
+
last_used: new Date().toISOString(),
|
|
219
|
+
created_at: new Date().toISOString(),
|
|
220
|
+
tenant_id: this.auth.tenantId
|
|
221
|
+
};
|
|
222
|
+
const { data, error } = await supabase
|
|
223
|
+
.from('ai_learnings')
|
|
224
|
+
.insert(newEntry)
|
|
225
|
+
.select()
|
|
226
|
+
.single();
|
|
227
|
+
if (!error && data) {
|
|
228
|
+
learningCache.addEntry(data);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Extract and update patterns
|
|
232
|
+
await this.extractAndUpdatePatterns(task, steps, result);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Extract patterns from successful executions
|
|
236
|
+
*/
|
|
237
|
+
async extractAndUpdatePatterns(task, steps, result) {
|
|
238
|
+
// Extract keywords from task
|
|
239
|
+
const keywords = this.extractKeywords(task);
|
|
240
|
+
// Try to find existing pattern
|
|
241
|
+
const existingPattern = learningCache.getAllPatterns().find(p => p.trigger_keywords.some(k => keywords.includes(k)));
|
|
242
|
+
if (existingPattern) {
|
|
243
|
+
// Add variation to existing pattern
|
|
244
|
+
const variation = {
|
|
245
|
+
task,
|
|
246
|
+
steps,
|
|
247
|
+
result,
|
|
248
|
+
timestamp: new Date().toISOString()
|
|
249
|
+
};
|
|
250
|
+
const variations = existingPattern.variations || [];
|
|
251
|
+
const updatedPattern = {
|
|
252
|
+
...existingPattern,
|
|
253
|
+
variations: [...variations, variation].slice(-10), // Keep last 10 variations
|
|
254
|
+
version: existingPattern.version + 1
|
|
255
|
+
};
|
|
256
|
+
await supabase
|
|
257
|
+
.from('ai_patterns')
|
|
258
|
+
.update(updatedPattern)
|
|
259
|
+
.eq('id', existingPattern.id);
|
|
260
|
+
learningCache.addPattern(updatedPattern);
|
|
261
|
+
}
|
|
262
|
+
else if (keywords.length > 0) {
|
|
263
|
+
// Create new pattern
|
|
264
|
+
const newPattern = {
|
|
265
|
+
pattern_name: `pattern_${this.hashString(task)}`,
|
|
266
|
+
trigger_keywords: keywords,
|
|
267
|
+
preconditions: {},
|
|
268
|
+
action_sequence: steps,
|
|
269
|
+
success_criteria: result,
|
|
270
|
+
variations: [],
|
|
271
|
+
optimization_hints: [],
|
|
272
|
+
version: 1,
|
|
273
|
+
tenant_id: this.auth.tenantId
|
|
274
|
+
};
|
|
275
|
+
const { data, error } = await supabase
|
|
276
|
+
.from('ai_patterns')
|
|
277
|
+
.insert(newPattern)
|
|
278
|
+
.select()
|
|
279
|
+
.single();
|
|
280
|
+
if (!error && data) {
|
|
281
|
+
learningCache.addPattern(data);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
extractKeywords(text) {
|
|
286
|
+
const stopWords = new Set(['o', 'a', 'de', 'para', 'com', 'em', 'do', 'da', 'um', 'uma']);
|
|
287
|
+
const words = text.toLowerCase()
|
|
288
|
+
.split(/\s+/)
|
|
289
|
+
.filter(w => w.length > 3 && !stopWords.has(w));
|
|
290
|
+
// Return unique significant words
|
|
291
|
+
return [...new Set(words)];
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Process user feedback on executions
|
|
295
|
+
*/
|
|
296
|
+
async processFeedback(taskSignature, feedbackType, details) {
|
|
297
|
+
const entry = learningCache.getEntry(taskSignature);
|
|
298
|
+
if (!entry)
|
|
299
|
+
return;
|
|
300
|
+
// Adjust confidence based on feedback
|
|
301
|
+
let confidenceAdjustment = 0;
|
|
302
|
+
switch (feedbackType) {
|
|
303
|
+
case 'success':
|
|
304
|
+
confidenceAdjustment = 0.1;
|
|
305
|
+
break;
|
|
306
|
+
case 'failure':
|
|
307
|
+
confidenceAdjustment = -0.2;
|
|
308
|
+
break;
|
|
309
|
+
case 'improvement':
|
|
310
|
+
confidenceAdjustment = 0.05;
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
const updatedEntry = {
|
|
314
|
+
...entry,
|
|
315
|
+
confidence_score: Math.max(0, Math.min(1, entry.confidence_score + confidenceAdjustment))
|
|
316
|
+
};
|
|
317
|
+
await supabase
|
|
318
|
+
.from('ai_learnings')
|
|
319
|
+
.update(updatedEntry)
|
|
320
|
+
.eq('id', entry.id);
|
|
321
|
+
// Store feedback record
|
|
322
|
+
await supabase
|
|
323
|
+
.from('ai_feedback')
|
|
324
|
+
.insert({
|
|
325
|
+
learning_id: entry.id,
|
|
326
|
+
feedback_type: feedbackType,
|
|
327
|
+
details,
|
|
328
|
+
created_at: new Date().toISOString(),
|
|
329
|
+
tenant_id: this.auth.tenantId
|
|
330
|
+
});
|
|
331
|
+
learningCache.addEntry(updatedEntry);
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Get learning statistics
|
|
335
|
+
*/
|
|
336
|
+
async getStatistics() {
|
|
337
|
+
await this.loadKnowledgeBase();
|
|
338
|
+
const entries = learningCache.getAllEntries();
|
|
339
|
+
const patterns = learningCache.getAllPatterns();
|
|
340
|
+
return {
|
|
341
|
+
total_learnings: entries.length,
|
|
342
|
+
total_patterns: patterns.length,
|
|
343
|
+
average_confidence: entries.length > 0 ? entries.reduce((sum, e) => sum + e.confidence_score, 0) / entries.length : 0,
|
|
344
|
+
most_used_tasks: entries
|
|
345
|
+
.sort((a, b) => b.usage_count - a.usage_count)
|
|
346
|
+
.slice(0, 5)
|
|
347
|
+
.map(e => ({ task: e.task_description, count: e.usage_count })),
|
|
348
|
+
recent_learnings: entries
|
|
349
|
+
.sort((a, b) => new Date(b.last_used).getTime() - new Date(a.last_used).getTime())
|
|
350
|
+
.slice(0, 5)
|
|
351
|
+
.map(e => ({ task: e.task_description, date: e.last_used }))
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Clean up old or low-confidence entries
|
|
356
|
+
*/
|
|
357
|
+
async pruneKnowledge() {
|
|
358
|
+
const thirtyDaysAgo = new Date();
|
|
359
|
+
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
|
|
360
|
+
// Remove entries with low confidence that haven't been used recently
|
|
361
|
+
await supabase
|
|
362
|
+
.from('ai_learnings')
|
|
363
|
+
.delete()
|
|
364
|
+
.eq('tenant_id', this.auth.tenantId)
|
|
365
|
+
.lt('confidence_score', 0.3)
|
|
366
|
+
.lt('last_used', thirtyDaysAgo.toISOString());
|
|
367
|
+
// Refresh cache
|
|
368
|
+
learningCache.clear();
|
|
369
|
+
await this.loadKnowledgeBase();
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
// ================ CONTEXT BUILDER ================
|
|
373
|
+
export class ContextBuilder {
|
|
374
|
+
learning;
|
|
375
|
+
constructor(learning) {
|
|
376
|
+
this.learning = learning;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Build enriched context for a task
|
|
380
|
+
*/
|
|
381
|
+
async buildContext(task) {
|
|
382
|
+
const knowledge = await this.learning.getRelevantKnowledge(task);
|
|
383
|
+
let context = '';
|
|
384
|
+
// Add similar tasks context
|
|
385
|
+
if (knowledge.similar_tasks.length > 0) {
|
|
386
|
+
context += 'EXPERIÊNCIAS ANTERIORES SIMILARES:\n';
|
|
387
|
+
for (const similar of knowledge.similar_tasks.slice(0, 3)) {
|
|
388
|
+
context += `- ${similar.task_description}\n`;
|
|
389
|
+
if (similar.solution_steps.length > 0) {
|
|
390
|
+
context += ` Passos: ${JSON.stringify(similar.solution_steps.slice(0, 3))}\n`;
|
|
391
|
+
}
|
|
392
|
+
context += ` Confiança: ${(similar.confidence_score * 100).toFixed(0)}%\n`;
|
|
393
|
+
}
|
|
394
|
+
context += '\n';
|
|
395
|
+
}
|
|
396
|
+
// Add patterns
|
|
397
|
+
if (knowledge.relevant_patterns.length > 0) {
|
|
398
|
+
context += 'PADRÕES IDENTIFICADOS:\n';
|
|
399
|
+
for (const pattern of knowledge.relevant_patterns) {
|
|
400
|
+
context += `- ${pattern.pattern_name}: `;
|
|
401
|
+
context += `${JSON.stringify(pattern.action_sequence.slice(0, 3))}\n`;
|
|
402
|
+
}
|
|
403
|
+
context += '\n';
|
|
404
|
+
}
|
|
405
|
+
// Add best practices
|
|
406
|
+
if (knowledge.best_practices.length > 0) {
|
|
407
|
+
context += 'MELHORES PRÁTICAS:\n';
|
|
408
|
+
for (const practice of knowledge.best_practices) {
|
|
409
|
+
context += `- ${practice}\n`;
|
|
410
|
+
}
|
|
411
|
+
context += '\n';
|
|
412
|
+
}
|
|
413
|
+
// Add warnings
|
|
414
|
+
if (knowledge.warnings.length > 0) {
|
|
415
|
+
context += 'AVISOS:\n';
|
|
416
|
+
for (const warning of knowledge.warnings) {
|
|
417
|
+
context += `⚠️ ${warning}\n`;
|
|
418
|
+
}
|
|
419
|
+
context += '\n';
|
|
420
|
+
}
|
|
421
|
+
return context;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Generate system prompt with accumulated knowledge
|
|
425
|
+
*/
|
|
426
|
+
async generateSystemPrompt(basePrompt) {
|
|
427
|
+
const stats = await this.learning.getStatistics();
|
|
428
|
+
let enhancedPrompt = basePrompt + '\n\n';
|
|
429
|
+
enhancedPrompt += '=== CONHECIMENTO ACUMULADO DO QUANTY ===\n\n';
|
|
430
|
+
if (stats.most_used_tasks.length > 0) {
|
|
431
|
+
enhancedPrompt += 'OPERAÇÕES MAIS COMUNS:\n';
|
|
432
|
+
for (const task of stats.most_used_tasks) {
|
|
433
|
+
enhancedPrompt += `- ${task.task}: ${task.count} vezes\n`;
|
|
434
|
+
}
|
|
435
|
+
enhancedPrompt += '\n';
|
|
436
|
+
}
|
|
437
|
+
enhancedPrompt += `Total de aprendizados: ${stats.total_learnings}\n`;
|
|
438
|
+
enhancedPrompt += `Padrões identificados: ${stats.total_patterns}\n`;
|
|
439
|
+
enhancedPrompt += `Confiança média: ${(stats.average_confidence * 100).toFixed(0)}%\n\n`;
|
|
440
|
+
enhancedPrompt += 'Use este conhecimento acumulado para fornecer respostas mais precisas e eficientes.\n';
|
|
441
|
+
return enhancedPrompt;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
// ================ EXPORT SINGLETON ================
|
|
445
|
+
let learningSystemInstance = null;
|
|
446
|
+
let contextBuilderInstance = null;
|
|
447
|
+
export function initLearningSystem(auth) {
|
|
448
|
+
learningSystemInstance = new QuantyLearningSystem(auth);
|
|
449
|
+
contextBuilderInstance = new ContextBuilder(learningSystemInstance);
|
|
450
|
+
}
|
|
451
|
+
export function getLearningSystem() {
|
|
452
|
+
if (!learningSystemInstance) {
|
|
453
|
+
throw new Error('Learning system not initialized. Call initLearningSystem first.');
|
|
454
|
+
}
|
|
455
|
+
return learningSystemInstance;
|
|
456
|
+
}
|
|
457
|
+
export function getContextBuilder() {
|
|
458
|
+
if (!contextBuilderInstance) {
|
|
459
|
+
throw new Error('Context builder not initialized. Call initLearningSystem first.');
|
|
460
|
+
}
|
|
461
|
+
return contextBuilderInstance;
|
|
462
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { AuthContext } from './auth.js';
|
|
2
|
+
/**
|
|
3
|
+
* Calcula similaridade combinada (ensemble)
|
|
4
|
+
*/
|
|
5
|
+
export declare function calculateSimilarity(a: string, b: string): {
|
|
6
|
+
overall: number;
|
|
7
|
+
fuzzy: number;
|
|
8
|
+
keyword: number;
|
|
9
|
+
tfidf: number;
|
|
10
|
+
category: {
|
|
11
|
+
a: string;
|
|
12
|
+
b: string;
|
|
13
|
+
match: boolean;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export interface MatchResult {
|
|
17
|
+
id: string;
|
|
18
|
+
code: string;
|
|
19
|
+
description: string;
|
|
20
|
+
unit: string;
|
|
21
|
+
unitCost: number;
|
|
22
|
+
source: string;
|
|
23
|
+
sourceId: string;
|
|
24
|
+
sourceName: string;
|
|
25
|
+
similarity: number;
|
|
26
|
+
similarityDetails: {
|
|
27
|
+
fuzzy: number;
|
|
28
|
+
keyword: number;
|
|
29
|
+
tfidf: number;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Busca itens similares em outros orçamentos
|
|
34
|
+
*/
|
|
35
|
+
export declare function findSimilarInBudgets(auth: AuthContext, description: string, excludeBudgetId?: string, limit?: number, minSimilarity?: number): Promise<MatchResult[]>;
|
|
36
|
+
/**
|
|
37
|
+
* Busca itens similares em bancos de composições
|
|
38
|
+
*/
|
|
39
|
+
export declare function findSimilarInBanks(auth: AuthContext, description: string, bankIds?: string[], limit?: number, minSimilarity?: number): Promise<MatchResult[]>;
|
|
40
|
+
/**
|
|
41
|
+
* Busca unificada em orçamentos e bancos
|
|
42
|
+
*/
|
|
43
|
+
export declare function findSimilarItems(auth: AuthContext, description: string, options?: {
|
|
44
|
+
searchBudgets?: boolean;
|
|
45
|
+
searchBanks?: boolean;
|
|
46
|
+
excludeBudgetId?: string;
|
|
47
|
+
bankIds?: string[];
|
|
48
|
+
limit?: number;
|
|
49
|
+
minSimilarity?: number;
|
|
50
|
+
}): Promise<{
|
|
51
|
+
budgetResults: MatchResult[];
|
|
52
|
+
bankResults: MatchResult[];
|
|
53
|
+
topMatches: MatchResult[];
|
|
54
|
+
}>;
|
|
55
|
+
/**
|
|
56
|
+
* Compara preços de itens similares
|
|
57
|
+
*/
|
|
58
|
+
export declare function comparePrices(auth: AuthContext, description: string, currentPrice: number, options?: {
|
|
59
|
+
excludeBudgetId?: string;
|
|
60
|
+
limit?: number;
|
|
61
|
+
minSimilarity?: number;
|
|
62
|
+
}): Promise<{
|
|
63
|
+
currentPrice: number;
|
|
64
|
+
matches: Array<MatchResult & {
|
|
65
|
+
priceDiff: number;
|
|
66
|
+
priceDiffPercent: number;
|
|
67
|
+
}>;
|
|
68
|
+
statistics: {
|
|
69
|
+
minPrice: number;
|
|
70
|
+
maxPrice: number;
|
|
71
|
+
avgPrice: number;
|
|
72
|
+
medianPrice: number;
|
|
73
|
+
recommendation: string;
|
|
74
|
+
};
|
|
75
|
+
}>;
|
|
76
|
+
//# sourceMappingURL=matchingService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matchingService.d.ts","sourceRoot":"","sources":["../../src/core/matchingService.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAwPxC;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;CACtD,CAkCA;AAID,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE;QACf,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;KACjB,CAAC;CACL;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACtC,IAAI,EAAE,WAAW,EACjB,WAAW,EAAE,MAAM,EACnB,eAAe,CAAC,EAAE,MAAM,EACxB,KAAK,GAAE,MAAW,EAClB,aAAa,GAAE,MAAW,GAC3B,OAAO,CAAC,WAAW,EAAE,CAAC,CAgDxB;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACpC,IAAI,EAAE,WAAW,EACjB,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,MAAM,EAAE,EAClB,KAAK,GAAE,MAAW,EAClB,aAAa,GAAE,MAAW,GAC3B,OAAO,CAAC,WAAW,EAAE,CAAC,CAqExB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAClC,IAAI,EAAE,WAAW,EACjB,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;IACL,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;CACrB,GACP,OAAO,CAAC;IACP,aAAa,EAAE,WAAW,EAAE,CAAC;IAC7B,WAAW,EAAE,WAAW,EAAE,CAAC;IAC3B,UAAU,EAAE,WAAW,EAAE,CAAC;CAC7B,CAAC,CA0BD;AAED;;GAEG;AACH,wBAAsB,aAAa,CAC/B,IAAI,EAAE,WAAW,EACjB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE;IACL,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;CACrB,GACP,OAAO,CAAC;IACP,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,KAAK,CAAC,WAAW,GAAG;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9E,UAAU,EAAE;QACR,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;KAC1B,CAAC;CACL,CAAC,CAqED"}
|