mycai 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/LICENSE +23 -0
- package/README.md +119 -0
- package/bin/mycai.js +156 -0
- package/core/ghr_lattice_memory.js +401 -0
- package/core/hdc.js +312 -0
- package/core/memory.js +60 -0
- package/core/memory_engine.js +444 -0
- package/core/morphology.js +357 -0
- package/core/phase.js +87 -0
- package/core/reasoning_router.js +628 -0
- package/core/resonance.js +118 -0
- package/core/safetensors.js +62 -0
- package/core/sovereign_agent.js +164 -0
- package/core/spectral.js +259 -0
- package/core/spectral_decoder.js +868 -0
- package/dist/resonance_sdk.js +2703 -0
- package/dist/resonance_sdk.min.js +14 -0
- package/dist/spectral_core.wasm +0 -0
- package/index.d.ts +45 -0
- package/index.js +26 -0
- package/package.json +52 -0
- package/sdk/README.md +154 -0
- package/sdk/examples/banking_assistant.js +61 -0
- package/sdk/examples/browser_example.html +104 -0
- package/sdk/examples/express_integration.js +61 -0
- package/sdk/examples/node_example.js +33 -0
- package/sdk/examples/quickstart.js +55 -0
- package/sdk/index.js +22 -0
- package/sdk/ingestion.js +237 -0
- package/sdk/licensing.js +168 -0
- package/sdk/resonance_engine.js +594 -0
- package/sdk/resonance_sdk.js +792 -0
- package/sdk/storage.js +185 -0
- package/sdk/tests/sdk_verification.js +98 -0
- package/sdk/tests/test_commercial_sdk.js +136 -0
- package/sdk/types.d.ts +152 -0
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resonance SDK v2.0 - B2B Commercial Enterprise AI Engine (@resonance/core)
|
|
3
|
+
* Air-Gapped, Sub-Millisecond Turkish Language Intelligence & Living Memory Engine.
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - 100% Offline Cryptographic Licensing (HMAC-SHA256 / Ed25519)
|
|
7
|
+
* - Cognitive Dispatcher (<0.05ms Math, 0.1ms Morphology, <3ms Semantic Resonance)
|
|
8
|
+
* - Precision Q&A Snippet Extractor & Concise Multi-Page Summarizer
|
|
9
|
+
* - Pluggable Storage Adapters (RAM, Disk/JSON, IndexedDB)
|
|
10
|
+
* - Zero-Bloat Modular Ingestion (Text, CSV, on-demand PDF/HTML)
|
|
11
|
+
*
|
|
12
|
+
* @license Commercial - Enterprise Infrastructure SDK
|
|
13
|
+
* @author Turkish Resonance AI Core Team
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { TurkishMorphology } from '../core/morphology.js';
|
|
17
|
+
import { HDCEngine, Representation } from '../core/hdc.js';
|
|
18
|
+
import { SpectralEngine } from '../core/spectral.js';
|
|
19
|
+
import { LicenseManager, LICENSE_TIERS } from './licensing.js';
|
|
20
|
+
import { MemoryStorageAdapter, FileStorageAdapter, IndexedDBStorageAdapter } from './storage.js';
|
|
21
|
+
import { IngestionEngine } from './ingestion.js';
|
|
22
|
+
import { GHRLatticeMemory } from '../core/ghr_lattice_memory.js';
|
|
23
|
+
|
|
24
|
+
// Safe Math Evaluator (Shunting-Yard, zero eval/Function)
|
|
25
|
+
function evalMathExpression(expr) {
|
|
26
|
+
const tokens = [];
|
|
27
|
+
let i = 0;
|
|
28
|
+
while (i < expr.length) {
|
|
29
|
+
if (/\s/.test(expr[i])) { i++; continue; }
|
|
30
|
+
if (/[0-9.]/.test(expr[i])) {
|
|
31
|
+
let n = '';
|
|
32
|
+
while (i < expr.length && /[0-9.]/.test(expr[i])) { n += expr[i]; i++; }
|
|
33
|
+
tokens.push({ t: 'N', v: parseFloat(n) });
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if ('+-*/%^()'.includes(expr[i])) {
|
|
37
|
+
tokens.push({ t: 'O', v: expr[i] });
|
|
38
|
+
i++;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
return NaN;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const prec = { '+': 1, '-': 1, '*': 2, '/': 2, '%': 2, '^': 3 };
|
|
45
|
+
const out = [];
|
|
46
|
+
const ops = [];
|
|
47
|
+
|
|
48
|
+
for (const tok of tokens) {
|
|
49
|
+
if (tok.t === 'N') out.push(tok.v);
|
|
50
|
+
else if (tok.v === '(') ops.push('(');
|
|
51
|
+
else if (tok.v === ')') {
|
|
52
|
+
while (ops.length && ops[ops.length - 1] !== '(') out.push(ops.pop());
|
|
53
|
+
ops.pop();
|
|
54
|
+
} else {
|
|
55
|
+
while (ops.length && ops[ops.length - 1] !== '(' && prec[ops[ops.length - 1]] >= prec[tok.v]) {
|
|
56
|
+
out.push(ops.pop());
|
|
57
|
+
}
|
|
58
|
+
ops.push(tok.v);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
while (ops.length) out.push(ops.pop());
|
|
62
|
+
|
|
63
|
+
const st = [];
|
|
64
|
+
for (const item of out) {
|
|
65
|
+
if (typeof item === 'number') st.push(item);
|
|
66
|
+
else {
|
|
67
|
+
const b = st.pop(), a = st.pop();
|
|
68
|
+
if (a === undefined || b === undefined) return NaN;
|
|
69
|
+
if (item === '+') st.push(a + b);
|
|
70
|
+
else if (item === '-') st.push(a - b);
|
|
71
|
+
else if (item === '*') st.push(a * b);
|
|
72
|
+
else if (item === '/') st.push(b === 0 ? NaN : a / b);
|
|
73
|
+
else if (item === '%') st.push(a % b);
|
|
74
|
+
else if (item === '^') st.push(Math.pow(a, b));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return st.length === 1 ? st[0] : NaN;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export class ResonanceEngine {
|
|
81
|
+
/**
|
|
82
|
+
* @param {Object} [config={}]
|
|
83
|
+
* @param {string} [config.licenseKey] - B2B offline cryptographic license key
|
|
84
|
+
* @param {number} [config.D=4096] - Hyperdimensional vector dimension
|
|
85
|
+
* @param {BaseStorageAdapter} [config.storage] - Pluggable memory storage adapter
|
|
86
|
+
* @param {string} [config.vendorSecret] - License validation secret (optional custom vendor salt)
|
|
87
|
+
*/
|
|
88
|
+
constructor(config = {}) {
|
|
89
|
+
this.D = config.D || 4096;
|
|
90
|
+
this.morphology = new TurkishMorphology();
|
|
91
|
+
this.hdc = new HDCEngine(this.D);
|
|
92
|
+
this.spectral = new SpectralEngine();
|
|
93
|
+
this.ingestion = new IngestionEngine(config.ingestion || {});
|
|
94
|
+
|
|
95
|
+
// Commercial Licensing Engine
|
|
96
|
+
this.licenseManager = new LicenseManager(config.vendorSecret);
|
|
97
|
+
this.licenseStatus = this.licenseManager.verifyLicense(config.licenseKey);
|
|
98
|
+
|
|
99
|
+
// Persistence Layer
|
|
100
|
+
this.storage = config.storage || new MemoryStorageAdapter();
|
|
101
|
+
|
|
102
|
+
// Gabor-Heisenberg Resonant Phase Lattice Memory Engine
|
|
103
|
+
this.ghrLattice = new GHRLatticeMemory({
|
|
104
|
+
D: this.D,
|
|
105
|
+
cellSize: config.latticeCellSize || 128,
|
|
106
|
+
sigma: config.latticeSigma || 16
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// In-memory living memory nodes
|
|
110
|
+
this._memoryStore = [];
|
|
111
|
+
this._lastActiveRecord = null;
|
|
112
|
+
this._conflictHandlers = [];
|
|
113
|
+
|
|
114
|
+
// Telemetry
|
|
115
|
+
this._stats = {
|
|
116
|
+
totalQueries: 0,
|
|
117
|
+
bypassedLlmCount: 0,
|
|
118
|
+
totalLatencyMs: 0,
|
|
119
|
+
startedAt: Date.now()
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
this._initialized = false;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Initialize the engine and load persistent memory
|
|
127
|
+
*/
|
|
128
|
+
async init() {
|
|
129
|
+
if (this._initialized) return this;
|
|
130
|
+
await this.storage.init();
|
|
131
|
+
const stored = await this.storage.load();
|
|
132
|
+
if (Array.isArray(stored) && stored.length > 0) {
|
|
133
|
+
this._memoryStore = stored;
|
|
134
|
+
}
|
|
135
|
+
this._initialized = true;
|
|
136
|
+
return this;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Universal Question / Answering / Command Dispatcher (< 0.1ms - 3ms)
|
|
141
|
+
* @param {string} prompt - User input query
|
|
142
|
+
* @param {Object} [options={}]
|
|
143
|
+
* @returns {Promise<{ answer: string, route: string, latencyMs: number, confidence: number, llmBypassed: boolean, metadata?: Object }>}
|
|
144
|
+
*/
|
|
145
|
+
async ask(prompt, options = {}) {
|
|
146
|
+
if (!this._initialized) await this.init();
|
|
147
|
+
const startTime = performance.now();
|
|
148
|
+
this._stats.totalQueries++;
|
|
149
|
+
|
|
150
|
+
const clean = (prompt || '').trim();
|
|
151
|
+
if (!clean) {
|
|
152
|
+
return {
|
|
153
|
+
answer: 'Lütfen bir soru veya metin giriniz.',
|
|
154
|
+
route: 'boundary',
|
|
155
|
+
latencyMs: 0.1,
|
|
156
|
+
confidence: 1.0,
|
|
157
|
+
llmBypassed: true
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// 0. Greeting Gate
|
|
162
|
+
const greetingRegex = /^(hey|merhaba|selam|sa|naber|nasılsın|günaydın|iyi akşamlar|iyi günler|hoşgeldin|hi|hello)\b/i;
|
|
163
|
+
if (greetingRegex.test(clean) && clean.split(/\s+/).length <= 3) {
|
|
164
|
+
const memCount = this._memoryStore.length;
|
|
165
|
+
const greetText = memCount > 0
|
|
166
|
+
? `Merhaba! Hafızamda ${memCount} adet kayıtlı bilgi var. Hangi konuda yardımcı olabilirim?`
|
|
167
|
+
: 'Merhaba! Size nasıl yardımcı olabilirim? Bilgi aktarmak için ingest() fonksiyonunu kullanabilirsiniz.';
|
|
168
|
+
return this._formatResponse(greetText, 'greeting', 1.0, startTime);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// 1. Deterministic Math Gate (AST Parser, ~0.02ms)
|
|
172
|
+
const mathRegex = /^[0-9+\-*/().\s%^]+$/;
|
|
173
|
+
if (mathRegex.test(clean) && /[+\-*/%^]/.test(clean) && this.licenseManager.canUseFeature('math')) {
|
|
174
|
+
const val = evalMathExpression(clean);
|
|
175
|
+
if (!isNaN(val) && isFinite(val)) {
|
|
176
|
+
return this._formatResponse(val.toString(), 'math', 1.0, startTime, { evaluatedMath: clean });
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// 2. Memory Listing Query ("başka ne biliyorsun", "neler var", "hangi konuda")
|
|
181
|
+
const listAllRegex = /başka ne|neler biliyorsun|hangi konuda|hafızanda neler var|kayıtlar/i;
|
|
182
|
+
const hasSubjectBefore = /\S+\s+(hakkında|ile ilgili|konusunda)\s*(ne biliyorsun)/i.test(clean);
|
|
183
|
+
if (listAllRegex.test(clean) && !hasSubjectBefore && this._memoryStore.length > 0) {
|
|
184
|
+
const seen = new Set();
|
|
185
|
+
const uniqueTitles = [];
|
|
186
|
+
for (const m of this._memoryStore) {
|
|
187
|
+
const title = (m.content || '').split('—')[0].trim().replace(/\s*—\s*Sayfa\s*\d+/i, '').trim();
|
|
188
|
+
if (!seen.has(title)) {
|
|
189
|
+
seen.add(title);
|
|
190
|
+
uniqueTitles.push('• ' + title);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const shown = uniqueTitles.slice(0, 10);
|
|
194
|
+
const suffix = uniqueTitles.length > 10 ? `\n… ve ${uniqueTitles.length - 10} farklı döküman daha.` : '';
|
|
195
|
+
const reply = `Hafızamda ${this._memoryStore.length} kayıt (${uniqueTitles.length} farklı döküman) var:\n${shown.join('\n')}${suffix}`;
|
|
196
|
+
return this._formatResponse(reply, 'memory_list', 0.95, startTime);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// 3. Living Memory Semantic Search Gate
|
|
200
|
+
if (this._memoryStore.length > 0 && this.licenseManager.canUseFeature('basic_memory')) {
|
|
201
|
+
const result = this._searchAndRouteMemory(clean);
|
|
202
|
+
if (result) {
|
|
203
|
+
return this._formatResponse(result.answer, result.route, result.confidence, startTime, result.metadata);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// 4. Turkish Morphology Gate (0.1ms)
|
|
208
|
+
const morphKeywords = /kök|ek|hece|ulama|harf|ünlü|ünsüz|fonoloji/i;
|
|
209
|
+
const words = clean.split(/\s+/).filter(Boolean);
|
|
210
|
+
if ((morphKeywords.test(clean) || (words.length === 1 && words[0].length > 4 && !/^[0-9]+$/.test(words[0]))) && this.licenseManager.canUseFeature('morphology')) {
|
|
211
|
+
const targetWord = words.length === 1 ? words[0] : words[words.length - 1].replace(/[?.!]/g, '');
|
|
212
|
+
const analysis = this.morphology.analyze(targetWord);
|
|
213
|
+
const text = `Morfolojik Analiz: "${targetWord}" kökü: "${analysis.root}", ekler: [${analysis.suffixes.join(', ') || 'yok'}], ünlü uyumu: "${analysis.harmony}", heceler: ${analysis.syllables.join('-')}`;
|
|
214
|
+
return this._formatResponse(text, 'morphology', 0.99, startTime, { analysis });
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// 5. Capability Boundary
|
|
218
|
+
if (this._memoryStore.length > 0) {
|
|
219
|
+
const sampleTitles = this._memoryStore.slice(0, 3).map(m => '• ' + (m.content || '').split('—')[0].trim()).join('\n');
|
|
220
|
+
return this._formatResponse(`Bu konuda kayıtlı bilgi bulamadım. Şu konularda soru sorabilirsiniz:\n${sampleTitles}`, 'boundary', 0.0, startTime);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return this._formatResponse('Henüz hafızamda kayıt yok. Döküman aktarmak için engine.ingest() fonksiyonunu kullanabilirsiniz.', 'boundary', 0.0, startTime);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Ingest documents, text, CSV, PDF into living memory
|
|
228
|
+
* @param {Object} input
|
|
229
|
+
* @returns {Promise<{ success: boolean, recordsAdded: number, stats: Object }>}
|
|
230
|
+
*/
|
|
231
|
+
async ingest(input) {
|
|
232
|
+
if (!this._initialized) await this.init();
|
|
233
|
+
|
|
234
|
+
// Check License Quota
|
|
235
|
+
const parsed = await this.ingestion.ingest(input);
|
|
236
|
+
if (!parsed.success || !parsed.records || parsed.records.length === 0) {
|
|
237
|
+
return { success: false, recordsAdded: 0, stats: parsed.stats };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (!this.licenseManager.checkNodeQuota(this._memoryStore.length, parsed.records.length)) {
|
|
241
|
+
throw new Error(`[Resonance License] Node quota exceeded! Tier: ${this.licenseManager.tier.name} (Max: ${this.licenseManager.maxNodes} nodes). Upgrade license to proceed.`);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
let addedCount = 0;
|
|
245
|
+
for (const r of parsed.records) {
|
|
246
|
+
// Check for conflicts
|
|
247
|
+
const existing = this._findConflict(r.content);
|
|
248
|
+
if (existing) {
|
|
249
|
+
this._handleConflict(existing, r);
|
|
250
|
+
}
|
|
251
|
+
this._memoryStore.push(r);
|
|
252
|
+
addedCount++;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
await this.storage.save(this._memoryStore);
|
|
256
|
+
return {
|
|
257
|
+
success: true,
|
|
258
|
+
recordsAdded: addedCount,
|
|
259
|
+
totalMemoryNodes: this._memoryStore.length,
|
|
260
|
+
stats: parsed.stats
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Generate a concise or single-sentence summary of a document or page
|
|
266
|
+
* @param {Object} opts
|
|
267
|
+
* @param {number} [opts.page]
|
|
268
|
+
* @param {string} [opts.document]
|
|
269
|
+
* @param {'concise'|'single'|'full'} [opts.mode='concise']
|
|
270
|
+
*/
|
|
271
|
+
summarize(opts = {}) {
|
|
272
|
+
const mode = opts.mode || 'concise';
|
|
273
|
+
const isSingle = mode === 'single';
|
|
274
|
+
|
|
275
|
+
let targetRecords = this._memoryStore;
|
|
276
|
+
if (opts.document) {
|
|
277
|
+
targetRecords = this._getDocumentRecords(opts.document);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (opts.page !== undefined) {
|
|
281
|
+
const pageMem = targetRecords.find(m => (m.content || '').toLowerCase().includes(`sayfa ${opts.page}`));
|
|
282
|
+
if (pageMem) {
|
|
283
|
+
return this._summarizeSinglePage(pageMem, opts.page, isSingle ? 'tek cümle' : 'özet');
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return this._buildStructuredSummary(targetRecords, []);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Search memory records semantically
|
|
292
|
+
* @param {string} query
|
|
293
|
+
* @param {number} [limit=5]
|
|
294
|
+
*/
|
|
295
|
+
search(query, limit = 5) {
|
|
296
|
+
const queryWords = this._extractQueryWords(query);
|
|
297
|
+
const scored = this._scoreRecords(this._memoryStore, queryWords);
|
|
298
|
+
return scored.slice(0, limit).map(s => ({
|
|
299
|
+
content: s.mem.content,
|
|
300
|
+
score: s.score,
|
|
301
|
+
metadata: s.mem.metadata
|
|
302
|
+
}));
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Clear all memory nodes
|
|
307
|
+
*/
|
|
308
|
+
async clearMemory() {
|
|
309
|
+
this._memoryStore = [];
|
|
310
|
+
this._lastActiveRecord = null;
|
|
311
|
+
await this.storage.clear();
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Register conflict resolution hook
|
|
317
|
+
* @param {Function} handler - (existingRecord, newRecord) => void
|
|
318
|
+
*/
|
|
319
|
+
onConflict(handler) {
|
|
320
|
+
if (typeof handler === 'function') {
|
|
321
|
+
this._conflictHandlers.push(handler);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Retrieve real-time telemetry metrics
|
|
327
|
+
*/
|
|
328
|
+
getTelemetry() {
|
|
329
|
+
const avgLatency = this._stats.totalQueries > 0
|
|
330
|
+
? (this._stats.totalLatencyMs / this._stats.totalQueries).toFixed(2)
|
|
331
|
+
: 0;
|
|
332
|
+
|
|
333
|
+
return {
|
|
334
|
+
activeNodes: this._memoryStore.length,
|
|
335
|
+
maxNodesQuota: this.licenseManager.maxNodes,
|
|
336
|
+
tier: this.licenseManager.tier.name,
|
|
337
|
+
licenseValid: this.licenseManager.isValid,
|
|
338
|
+
totalQueries: this._stats.totalQueries,
|
|
339
|
+
avgLatencyMs: Number(avgLatency),
|
|
340
|
+
bypassedLlmCount: this._stats.bypassedLlmCount,
|
|
341
|
+
storageType: this.storage.name,
|
|
342
|
+
uptimeSeconds: Math.floor((Date.now() - this._stats.startedAt) / 1000)
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// ── Internal Routing Helpers ─────────────────────────────────
|
|
347
|
+
|
|
348
|
+
_searchAndRouteMemory(clean) {
|
|
349
|
+
const isSummaryQuery = /özet|özeet|özeti|özetle|özetini|özetler|kısaca/i.test(clean);
|
|
350
|
+
const pageMatch = clean.match(/(\d+)\s*\.?\s*(sayfa|sayfanın|sayfayı|sayfası)/i) ||
|
|
351
|
+
clean.match(/(sayfa|sayfanın)\s*(\d+)/i);
|
|
352
|
+
const isDetailQuery = /nedir|kaç|ne kadar|hangi|nasıl|kim|neden|nere|sayısı|sayisi|listesi|listele|mesaj|hedef|kanal/i.test(clean);
|
|
353
|
+
|
|
354
|
+
const queryWords = this._extractQueryWords(clean);
|
|
355
|
+
const scoredMems = this._scoreRecords(this._memoryStore, queryWords);
|
|
356
|
+
|
|
357
|
+
const bestScore = scoredMems.length > 0 ? scoredMems[0].score : 0;
|
|
358
|
+
const bestMatch = scoredMems.length > 0 ? scoredMems[0].mem : null;
|
|
359
|
+
|
|
360
|
+
// Direct / Contextual target selection
|
|
361
|
+
let activeMems = [];
|
|
362
|
+
if (bestScore >= 3 && bestMatch) {
|
|
363
|
+
this._lastActiveRecord = bestMatch;
|
|
364
|
+
activeMems = scoredMems.filter(s => s.score >= 3).map(s => s.mem);
|
|
365
|
+
} else if (this._lastActiveRecord && (isDetailQuery || isSummaryQuery || pageMatch)) {
|
|
366
|
+
activeMems = this._getDocumentRecords(this._lastActiveRecord.content);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (activeMems.length === 0) return null;
|
|
370
|
+
|
|
371
|
+
// Page-specific query ("3. sayfayı özetle", "6. sayfayı tek cümlede özetle")
|
|
372
|
+
if (pageMatch) {
|
|
373
|
+
const pageNum = parseInt(pageMatch[1], 10) || parseInt(pageMatch[2], 10);
|
|
374
|
+
const pageMem = activeMems.find(m => (m.content || '').toLowerCase().includes(`sayfa ${pageNum}`));
|
|
375
|
+
if (pageMem) {
|
|
376
|
+
this._lastActiveRecord = pageMem;
|
|
377
|
+
const answer = this._summarizeSinglePage(pageMem, pageNum, clean);
|
|
378
|
+
return { answer, route: 'memory_page_summary', confidence: 0.95 };
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Detail Q&A Query ("asliye hukuk mahkemelerinin görevi nedir?")
|
|
383
|
+
if (isDetailQuery && !isSummaryQuery) {
|
|
384
|
+
const answer = this._findRelevantSnippet(activeMems, queryWords, clean);
|
|
385
|
+
return { answer, route: 'memory_qa_detail', confidence: 0.92 };
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Explicit Document Summary Query ("opacus özeti", "özet çıkar")
|
|
389
|
+
if (isSummaryQuery) {
|
|
390
|
+
const answer = this._buildStructuredSummary(activeMems.slice(0, 8), queryWords);
|
|
391
|
+
return { answer, route: 'memory_structured_summary', confidence: 0.90 };
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// General Overview Query
|
|
395
|
+
if (activeMems.length > 1) {
|
|
396
|
+
const answer = this._buildQuickOverview(activeMems, queryWords);
|
|
397
|
+
return { answer, route: 'memory_quick_overview', confidence: 0.88 };
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return {
|
|
401
|
+
answer: bestMatch.content,
|
|
402
|
+
route: 'memory_match',
|
|
403
|
+
confidence: 0.85
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
_extractQueryWords(clean) {
|
|
408
|
+
const lower = clean.toLowerCase()
|
|
409
|
+
.replace(/[?.!,;]/g, '')
|
|
410
|
+
.replace(/(hakkında|ne biliyorsun|ne bilirsun|özetle|özeet|özetini|özet|çıkar|çıkarır|mısın|oluştur|ver|geç|anlat|açıkla|söyle|nedir|neydi|göster|listele|bul|var mı|hatırla|hatırlıyor|başka|ile ilgili|bunun|neler)/gi, '')
|
|
411
|
+
.trim();
|
|
412
|
+
const stopWords = new Set(['ne', 'mi', 'mı', 'mu', 'mü', 've', 'ile', 'bir', 'de', 'da', 'bu', 'şu', 'o', 'en', 'çok', 'az', 'var', 'yok']);
|
|
413
|
+
return lower.split(/\s+/).filter(w => w.length >= 2 && !stopWords.has(w));
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
_scoreRecords(records, queryWords) {
|
|
417
|
+
const scored = [];
|
|
418
|
+
for (const mem of records) {
|
|
419
|
+
const targetText = ((mem.content || '') + ' ' + (mem.query || '')).toLowerCase();
|
|
420
|
+
let score = 0;
|
|
421
|
+
for (const w of queryWords) {
|
|
422
|
+
if (targetText.includes(w)) score += w.length * 2;
|
|
423
|
+
else {
|
|
424
|
+
const stem = w.slice(0, Math.max(3, Math.floor(w.length * 0.7)));
|
|
425
|
+
if (targetText.includes(stem)) score += stem.length;
|
|
426
|
+
}
|
|
427
|
+
if ((mem.content || '').toLowerCase().split('—')[0].includes(w)) score += w.length;
|
|
428
|
+
}
|
|
429
|
+
if (score > 0) scored.push({ mem, score });
|
|
430
|
+
}
|
|
431
|
+
return scored.sort((a, b) => b.score - a.score);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
_findRelevantSnippet(mems, queryWords, originalQuery) {
|
|
435
|
+
const allSentences = [];
|
|
436
|
+
for (const mem of mems) {
|
|
437
|
+
const text = this._cleanContent(mem.content);
|
|
438
|
+
const sents = text.split(/[.!?;]\s+/).map(s => s.trim()).filter(s => s.length > 15 && s.length < 350);
|
|
439
|
+
allSentences.push(...sents);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const detailKeywords = originalQuery.toLowerCase()
|
|
443
|
+
.replace(/(nedir|nelerdir|kaçtır|kaç|ne kadar|hangi|nasıl|kim|neden|nere|sayısı|listesi|listele|hakkında|söyle|anlat|göster|ver)/gi, '')
|
|
444
|
+
.replace(/[?.!,;]/g, '')
|
|
445
|
+
.trim()
|
|
446
|
+
.split(/\s+/)
|
|
447
|
+
.filter(w => w.length >= 2);
|
|
448
|
+
|
|
449
|
+
const keywords = [...new Set([...queryWords, ...detailKeywords])];
|
|
450
|
+
const scored = allSentences.map(s => {
|
|
451
|
+
const lower = s.toLowerCase();
|
|
452
|
+
let score = 0;
|
|
453
|
+
for (const w of keywords) {
|
|
454
|
+
if (lower.includes(w)) score += w.length * 3;
|
|
455
|
+
}
|
|
456
|
+
return { text: s, score };
|
|
457
|
+
}).sort((a, b) => b.score - a.score);
|
|
458
|
+
|
|
459
|
+
const top = scored.filter(s => s.score > 0).slice(0, 3);
|
|
460
|
+
if (top.length === 0) {
|
|
461
|
+
return 'Bu detay hakkında kayıtlarda doğrudan bilgi bulunamadı.';
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const docTitle = (mems[0].content || '').split('—')[0].trim();
|
|
465
|
+
return `📌 ${docTitle} — Cevap:\n\n` + top.map(s => '• ' + s.text).join('\n');
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
_summarizeSinglePage(mem, pageNum, userQuery) {
|
|
469
|
+
const raw = this._cleanContent(mem.content);
|
|
470
|
+
const isSingle = /tek cümle|bir cümle|kısaca tek|özeti tek/i.test(userQuery);
|
|
471
|
+
|
|
472
|
+
const cleanText = raw
|
|
473
|
+
.replace(/Subject:.*$/gmi, '')
|
|
474
|
+
.replace(/Hi\s+[A-Za-z0-9\s]+team,.*$/gmi, '')
|
|
475
|
+
.replace(/https?:\/\/\S+/gi, '')
|
|
476
|
+
.replace(/Link:\s*\|?/gi, '')
|
|
477
|
+
.trim();
|
|
478
|
+
|
|
479
|
+
const sents = cleanText.split(/(?<=[.!?])\s+|[\n\r]+/)
|
|
480
|
+
.map(s => s.replace(/^[\s•\-\d.)]+/, '').trim())
|
|
481
|
+
.filter(s => s.length > 25 && s.length < 260 && !/^(subject|hi |link:|mesaj:)/i.test(s));
|
|
482
|
+
|
|
483
|
+
if (sents.length === 0) {
|
|
484
|
+
return `📄 Sayfa ${pageNum} Özeti:\n\n• ` + cleanText.slice(0, 150) + '...';
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const scored = sents.map(s => {
|
|
488
|
+
let score = 0;
|
|
489
|
+
if (/[çğıöşüÇĞİÖŞÜ]/.test(s)) score += 4;
|
|
490
|
+
if (/nedir|amaç|sağlar|sunar|özellik|entegrasyon|görev|hedef|plan|başarı|metrik/i.test(s)) score += 6;
|
|
491
|
+
if (s.length >= 45 && s.length <= 180) score += 3;
|
|
492
|
+
return { text: s, score };
|
|
493
|
+
}).sort((a, b) => b.score - a.score);
|
|
494
|
+
|
|
495
|
+
if (isSingle) {
|
|
496
|
+
const best = scored[0] ? scored[0].text : sents[0];
|
|
497
|
+
return `📄 Sayfa ${pageNum} (Tek Cümle Özet):\n\n${best}${best.endsWith('.') ? '' : '.'}`;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const top = scored.slice(0, 3).map(s => '• ' + s.text);
|
|
501
|
+
return `📄 Sayfa ${pageNum} Özeti:\n\n` + top.join('\n');
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
_buildStructuredSummary(mems, queryWords) {
|
|
505
|
+
if (!mems || mems.length === 0) return 'Özetlenecek döküman bulunamadı.';
|
|
506
|
+
const docTitle = (mems[0].content || '').split('—')[0].trim();
|
|
507
|
+
const allText = mems.map(m => this._cleanContent(m.content)).join(' ');
|
|
508
|
+
|
|
509
|
+
const dateMatches = allText.match(/\d{1,2}\s+(Ocak|Şubat|Mart|Nisan|Mayıs|Haziran|Temmuz|Ağustos|Eylül|Ekim|Kasım|Aralık)\s+\d{4}|\d{4}[-/.]\d{1,2}[-/.]\d{1,2}/gi);
|
|
510
|
+
const dates = dateMatches ? [...new Set(dateMatches)].slice(0, 3) : [];
|
|
511
|
+
|
|
512
|
+
const numMatches = allText.match(/\d{1,3}([.,]\d{3})*\s*(USD|TL|₺|\$|developer|kullanıcı|takipçi|kişi|proje|K\b|%)/gi);
|
|
513
|
+
const nums = numMatches ? [...new Set(numMatches.map(n => n.trim()))].slice(0, 4) : [];
|
|
514
|
+
|
|
515
|
+
const sents = allText.split(/[.!?;]\s+/).map(s => s.trim()).filter(s => s.length > 25 && s.length < 240);
|
|
516
|
+
const topSents = sents.slice(0, 5).map(s => '• ' + s);
|
|
517
|
+
|
|
518
|
+
let res = `📋 ${docTitle}\n`;
|
|
519
|
+
if (dates.length > 0) res += `📅 Tarih: ${dates.join(', ')}\n`;
|
|
520
|
+
if (nums.length > 0) res += `📈 Önemli Rakamlar: ${nums.join(' · ')}\n`;
|
|
521
|
+
res += `\n🔑 Özet (${mems.length} kayıt):\n` + topSents.join('\n');
|
|
522
|
+
return res;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
_buildQuickOverview(mems, queryWords) {
|
|
526
|
+
const docTitle = (mems[0].content || '').split('—')[0].trim();
|
|
527
|
+
const firstText = this._cleanContent(mems[0].content);
|
|
528
|
+
const sents = firstText.split(/[.!?;]\s+/).map(s => s.trim()).filter(s => s.length > 25).slice(0, 2);
|
|
529
|
+
|
|
530
|
+
let res = `📋 ${docTitle} (${mems.length} sayfa/bölüm)\n\n`;
|
|
531
|
+
if (sents.length > 0) res += sents.join('. ') + '.\n\n';
|
|
532
|
+
res += '💡 Detay veya sayfa özeti için soru sorabilirsiniz.';
|
|
533
|
+
return res;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
_cleanContent(content = '') {
|
|
537
|
+
const parts = content.split('—');
|
|
538
|
+
const raw = parts.length >= 3 ? parts.slice(2).join('—') : (parts[1] || parts[0]);
|
|
539
|
+
return raw
|
|
540
|
+
.replace(/\bfi\b/g, 'fi').replace(/\bfl\b/g, 'fl')
|
|
541
|
+
.replace(/\s{2,}/g, ' ')
|
|
542
|
+
.trim();
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
_getDocumentRecords(docIdentifier) {
|
|
546
|
+
const base = docIdentifier.split('—')[0].trim().toLowerCase();
|
|
547
|
+
return this._memoryStore.filter(m => (m.content || '').toLowerCase().startsWith(base));
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
_findConflict(content) {
|
|
551
|
+
const title = content.split('—')[0].trim();
|
|
552
|
+
return this._memoryStore.find(m => {
|
|
553
|
+
const mTitle = (m.content || '').split('—')[0].trim();
|
|
554
|
+
return mTitle === title && m.content !== content;
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
_handleConflict(existing, incoming) {
|
|
559
|
+
for (const h of this._conflictHandlers) {
|
|
560
|
+
try { h(existing, incoming); } catch(e) {}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
_formatResponse(answer, route, confidence, startTime, metadata = {}) {
|
|
565
|
+
const latency = Number((performance.now() - startTime).toFixed(2));
|
|
566
|
+
this._stats.totalLatencyMs += latency;
|
|
567
|
+
this._stats.bypassedLlmCount++;
|
|
568
|
+
|
|
569
|
+
return {
|
|
570
|
+
answer,
|
|
571
|
+
route,
|
|
572
|
+
confidence,
|
|
573
|
+
latencyMs: latency,
|
|
574
|
+
llmBypassed: true,
|
|
575
|
+
tier: this.licenseManager.tier.name,
|
|
576
|
+
metadata
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Factory to create an isolated GHR-Lattice memory engine instance.
|
|
582
|
+
* @param {Object} [config]
|
|
583
|
+
* @returns {GHRLatticeMemory}
|
|
584
|
+
*/
|
|
585
|
+
createLatticeMemory(config = {}) {
|
|
586
|
+
return new GHRLatticeMemory({
|
|
587
|
+
D: this.D,
|
|
588
|
+
...config
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
export { GHRLatticeMemory };
|
|
594
|
+
export const ResonanceSDK = ResonanceEngine;
|