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.
@@ -0,0 +1,792 @@
1
+ /**
2
+ * Resonance SDK v1.0 - B2B Commercial Edge AI Engine
3
+ * Unified SDK interface wrapping the Turkish Resonance AI Core (v5.2).
4
+ * Supports both Browser (fetch/streaming) and Node.js environments.
5
+ *
6
+ * @license Commercial - Per-device licensing
7
+ * @author Turkish Resonance AI Core Team
8
+ */
9
+
10
+ import { TurkishMorphology } from '../core/morphology.js';
11
+ import { HDCEngine, Representation } from '../core/hdc.js';
12
+ import { MemoryEngine } from '../core/memory_engine.js';
13
+ import { ReasoningRouter } from '../core/reasoning_router.js';
14
+ import { SovereignAgent } from '../core/sovereign_agent.js';
15
+ import { SpectralEngine } from '../core/spectral.js';
16
+ import { GHRLatticeMemory } from '../core/ghr_lattice_memory.js';
17
+
18
+
19
+ // Suffix derivation generator for expanding vocabulary with Turkish grammatical rules
20
+ function generateDerivations(root, morphology) {
21
+ const harmony = morphology.determineVowelHarmony(root);
22
+ const lastChar = root[root.length - 1];
23
+ const isVowel = morphology.allVowels.has(lastChar);
24
+ const derivations = [root];
25
+ const hardConsonants = new Set(['t', 'k', 'ç', 'p', 's', 'ş', 'h', 'f']);
26
+ const isHard = hardConsonants.has(lastChar);
27
+
28
+ // Helper to mutate root ending with p, ç, t, k when appending a vowel-starting suffix
29
+ const mutateRoot = (r, suffix) => {
30
+ if (!suffix) return r;
31
+ const startsWithVowel = morphology.allVowels.has(suffix[0]);
32
+ if (startsWithVowel) {
33
+ const last = r[r.length - 1];
34
+ let mutated = r.slice(0, -1);
35
+ if (last === 'p') return mutated + 'b' + suffix;
36
+ if (last === 'ç') return mutated + 'c' + suffix;
37
+ if (last === 't') return mutated + 'd' + suffix;
38
+ if (last === 'k') {
39
+ // e.g., renk -> rengi (g), but bebek -> bebeği (ğ)
40
+ if (r === 'renk') return mutated + 'g' + suffix;
41
+ return mutated + 'ğ' + suffix;
42
+ }
43
+ }
44
+ return r + suffix;
45
+ };
46
+
47
+ if (harmony === 'front') {
48
+ derivations.push(root + 'ler');
49
+ derivations.push(isVowel ? root + 'nin' : mutateRoot(root, 'in'));
50
+ derivations.push(isVowel ? root + 'ye' : mutateRoot(root, 'e'));
51
+ derivations.push(isVowel ? root + 'yi' : mutateRoot(root, 'i'));
52
+ derivations.push(root + (isHard ? 'te' : 'de'));
53
+ derivations.push(root + (isHard ? 'ten' : 'den'));
54
+ derivations.push(isVowel ? root + 'm' : mutateRoot(root, 'im'));
55
+ } else {
56
+ derivations.push(root + 'lar');
57
+ derivations.push(isVowel ? root + 'nın' : mutateRoot(root, 'ın'));
58
+ derivations.push(isVowel ? root + 'ya' : mutateRoot(root, 'a'));
59
+ derivations.push(isVowel ? root + 'yı' : mutateRoot(root, 'ı'));
60
+ derivations.push(root + (isHard ? 'ta' : 'da'));
61
+ derivations.push(root + (isHard ? 'tan' : 'dan'));
62
+ derivations.push(isVowel ? root + 'm' : mutateRoot(root, 'ım'));
63
+ }
64
+ return derivations;
65
+ }
66
+
67
+ export class ResonanceSDK {
68
+ /**
69
+ * @param {Object} config - SDK configuration
70
+ * @param {number} [config.D=4096] - Hyperdimensional vector dimension
71
+ * @param {number} [config.temperature=0.7] - Default sampling temperature
72
+ * @param {number} [config.maxLength=10] - Default max generation length
73
+ */
74
+ constructor(config = {}) {
75
+ this.D = config.D || 4096;
76
+ this.hdc = new HDCEngine(this.D);
77
+ this.morphology = new TurkishMorphology();
78
+ this.spectral = new SpectralEngine();
79
+ this.memory = new MemoryEngine(config.memory || {});
80
+ this.router = new ReasoningRouter(this.memory, this);
81
+
82
+ this.temperature = config.temperature !== undefined ? config.temperature : 0.7;
83
+ this.maxLength = config.maxLength || 10;
84
+
85
+ // Gabor-Heisenberg Resonant Phase Lattice Memory Engine
86
+ this.ghrLattice = new GHRLatticeMemory({
87
+ D: this.D,
88
+ cellSize: config.latticeCellSize || 128,
89
+ sigma: config.latticeSigma || 16
90
+ });
91
+
92
+ this.vocab = [];
93
+ this.vocabMap = new Map();
94
+ this.embeddings = new Map();
95
+ this.wordCounts = new Map();
96
+ this.freqIndices = [];
97
+ this.wordDerivationIndices = new Map();
98
+ this.transitionCounts = new Map();
99
+ this.sparseWeightsSpec = null;
100
+
101
+ this.wasmInstance = null;
102
+ this.wasmMemory = null;
103
+ this.vocabCosPtr = 0;
104
+ this.vocabSinPtr = 0;
105
+ this.scoresPtr = 0;
106
+ this.candidateIndicesPtr = 0;
107
+
108
+ this._initialized = false;
109
+ this._runtime = typeof window !== 'undefined' ? 'browser' : 'node';
110
+ }
111
+
112
+ // ── PUBLIC API ──────────────────────────────────────────────
113
+
114
+ /**
115
+ * Initialize the SDK. Must be called before generate() or analyze().
116
+ * @param {Object} [options]
117
+ * @param {ArrayBuffer} [options.wasmBinary] - Pre-loaded WASM binary buffer
118
+ * @param {string} [options.wasmUrl] - URL to fetch WASM (browser mode)
119
+ * @param {string} [options.wasmPath] - File path to WASM (Node.js mode)
120
+ * @param {string} [options.corpusUrl] - URL to tr_corpus_embed.js (browser mode)
121
+ * @param {string} [options.corpusPath] - File path to tr_corpus_embed.js (Node.js mode)
122
+ */
123
+ async init(options = {}) {
124
+ if (this._initialized) return;
125
+
126
+ // 1. Load WASM core
127
+ await this._loadWasm(options);
128
+
129
+ // 2. Build vocabulary, embeddings, and transition model
130
+ await this._buildVocabulary(options);
131
+
132
+ this._initialized = true;
133
+ }
134
+
135
+ /**
136
+ * Generate text autoregressively from a prompt.
137
+ * @param {string} prompt - Input Turkish text prompt
138
+ * @param {Object} [options]
139
+ * @param {number} [options.maxLength] - Override default max tokens
140
+ * @param {number} [options.temperature] - Override default temperature
141
+ * @param {function} [options.onToken] - Streaming callback: (word, step) => void
142
+ * @returns {Object} { prompt, generatedText, newTokens, steps, totalLatencyMs, finishReason, vocabSize }
143
+ */
144
+ generate(prompt, options = {}) {
145
+ this._assertInit();
146
+
147
+ const maxLen = options.maxLength || this.maxLength;
148
+ const temp = options.temperature !== undefined ? options.temperature : this.temperature;
149
+ const onToken = options.onToken || null;
150
+
151
+ const cleanPrompt = this.morphology.normalize(prompt);
152
+ const tokens = cleanPrompt.split(/\s+/).filter(Boolean);
153
+ if (tokens.length === 0) tokens.push('ev');
154
+
155
+ const promptLength = tokens.length;
156
+ const steps = [];
157
+ let stopReason = 'length';
158
+ const startTime = performance.now();
159
+
160
+ for (let step = 0; step < maxLen; step++) {
161
+ const stepStart = performance.now();
162
+ const nextObj = this._predictNext(tokens, temp, promptLength);
163
+ const stepEnd = performance.now();
164
+
165
+ tokens.push(nextObj.word);
166
+ const stepInfo = { word: nextObj.word, score: nextObj.score, latencyMs: stepEnd - stepStart };
167
+ steps.push(stepInfo);
168
+
169
+ if (onToken) onToken(nextObj.word, stepInfo);
170
+
171
+ // Predicate early stopping
172
+ if (this._isTerminal(nextObj.word)) { stopReason = 'stop'; break; }
173
+
174
+ // Repetition guard
175
+ if (tokens.length > promptLength + 2 && tokens.slice(-3).every(v => v === tokens[tokens.length - 1])) {
176
+ stopReason = 'stop'; break;
177
+ }
178
+ }
179
+
180
+ return {
181
+ prompt,
182
+ generatedText: tokens.join(' '),
183
+ newTokens: tokens.slice(promptLength),
184
+ steps,
185
+ totalLatencyMs: performance.now() - startTime,
186
+ finishReason: stopReason,
187
+ vocabSize: this.vocab.length
188
+ };
189
+ }
190
+
191
+ /**
192
+ * Analyze Turkish word morphology.
193
+ * @param {string} word - Turkish word to analyze
194
+ * @returns {Object} { word, root, suffixes, morphemes, harmony, syllables }
195
+ */
196
+ analyze(word) {
197
+ return this.morphology.analyze(word);
198
+ }
199
+
200
+ /**
201
+ * Get SDK runtime metrics.
202
+ * @returns {Object} { runtime, vocabSize, dimension, wasmActive, initialized }
203
+ */
204
+ getMetrics() {
205
+ return {
206
+ runtime: this._runtime,
207
+ vocabSize: this.vocab.length,
208
+ dimension: this.D,
209
+ wasmActive: this.wasmInstance !== null,
210
+ initialized: this._initialized
211
+ };
212
+ }
213
+
214
+ /**
215
+ * Instantiate an autonomous SovereignAgent.
216
+ * @returns {SovereignAgent}
217
+ */
218
+ createAgent() {
219
+ return new SovereignAgent(this);
220
+ }
221
+
222
+ // ── PRIVATE: WASM LOADER ───────────────────────────────────
223
+
224
+ async _loadWasm(options) {
225
+ const imports = { env: { cosf: Math.cos, sinf: Math.sin, atan2f: Math.atan2, expf: Math.exp, sqrtf: Math.sqrt } };
226
+ try {
227
+ if (options.wasmBinary) {
228
+ const mod = new WebAssembly.Module(options.wasmBinary);
229
+ this.wasmInstance = new WebAssembly.Instance(mod, imports);
230
+ this.wasmMemory = this.wasmInstance.exports.memory;
231
+ } else if (this._runtime === 'browser') {
232
+ const url = options.wasmUrl || '../wasm/spectral_core.wasm';
233
+ const res = await fetch(url);
234
+ const buf = await res.arrayBuffer();
235
+ const mod = new WebAssembly.Module(buf);
236
+ this.wasmInstance = new WebAssembly.Instance(mod, imports);
237
+ this.wasmMemory = this.wasmInstance.exports.memory;
238
+ } else {
239
+ const fs = await import('fs');
240
+ const path = await import('path');
241
+ const { fileURLToPath } = await import('url');
242
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
243
+ const wasmPath = options.wasmPath || path.join(__dirname, '..', 'wasm', 'spectral_core.wasm');
244
+ if (fs.existsSync(wasmPath)) {
245
+ const buf = fs.readFileSync(wasmPath);
246
+ const mod = new WebAssembly.Module(buf);
247
+ this.wasmInstance = new WebAssembly.Instance(mod, imports);
248
+ this.wasmMemory = this.wasmInstance.exports.memory;
249
+ }
250
+ }
251
+ } catch (e) {
252
+ console.warn('ResonanceSDK: WASM load failed, Pure JS fallback active.', e.message);
253
+ }
254
+ }
255
+
256
+ // ── PRIVATE: VOCABULARY BUILDER ────────────────────────────
257
+
258
+ async _buildVocabulary(options) {
259
+ // Training corpus (embedded for zero-dependency deployment)
260
+ const corpus = [
261
+ // ── Orijinal genel corpus ──
262
+ "evimizden yeni çıktık", "yeni bir kitap aldım",
263
+ "bilimsel araştırmalar yapay zeka ile hızlandı", "güzel bir gün başladı",
264
+ "iyi bir insan olmak önemlidir", "türkçe dil yapısı çok zengindir",
265
+ "yapay zeka insan beyni gibi çalışır", "öğrenmek ve düşünmek zihni geliştirir",
266
+ "büyük bir adım attık", "yeni projeler üzerinde çalışıyoruz",
267
+ "okuma alışkanlığı kazanmak önemlidir", "bilim ve teknik dünyayı değiştiriyor",
268
+ "evimizden okula kadar yürüdük", "yapay zeka dil modelleri üzerine kuruludur",
269
+ "yeni bir dünya bizi bekliyor", "kitaplar en iyi arkadaştır",
270
+ "güzel bir gelecek inşa ediyoruz", "iyi bir eğitim almak önemlidir",
271
+ "türkçe konuşmak ve yazmak çok güzel", "bilimsel gerçekler her zaman kazanır",
272
+ "yapay sinir ağları karmaşık modellerdir", "beyin ve zihin araştırmaları sürüyor",
273
+ "evimizden yeni bir yola çıktık", "yeni bir başlangıç yapmak iyidir",
274
+ "bilimsel okuma yapmak zihni açar", "okuma yapmak insanı geliştirir",
275
+ "evimizden çıktık ve okula gittik", "yeni bir güne uyandık",
276
+ "bilim insanları yapay zeka geliştiriyor", "türkçe dil bilgisi kuralları önemlidir",
277
+ "evimizden okula gittik yeni bir kitap aldık",
278
+ "televizyonu açıp haberleri izledik", "arabaya binip okula gittik",
279
+ "bilgisayarı kapatıp uyudum", "bilgisayarı kapatıp yattım",
280
+ "yazılım mühendisleri yapay zeka modelleri üzerine araştırma yapıyor",
281
+ "bilgi teknolojileri ve veri analizi iş süreçlerini kolaylaştırır",
282
+ "sağlıklı beslenme ve spor yapmak yaşam kalitesini artırır",
283
+ "doğal yaşamı ve çevreyi korumak hepimizin sorumluluğundadır",
284
+ "sanat ve edebiyat toplumun kültürel zenginliğini besler ve geliştirir",
285
+ "eğitim sistemi yeni nesillerin geleceğini ve başarısını belirler",
286
+ "bilgisayar ağları veri güvenliği ve hızlı bilgi akışı sağlar",
287
+ // ── Selamlama ve bağlam kurma ──
288
+ "merhaba size nasıl yardımcı olabilirim",
289
+ "günaydın bugün size nasıl yardımcı olayım",
290
+ "iyi günler lütfen sorunuzu belirtin",
291
+ "hoş geldiniz nasıl yardımcı olabilirim",
292
+ "merhaba buyurun nasıl yardımcı olayım",
293
+ "iyi akşamlar size nasıl yardımcı olabilirim",
294
+ // ── Hasta kaydı kalıpları ──
295
+ "hasta kaydı oluşturuldu",
296
+ "kayıt sisteme başarıyla eklendi",
297
+ "hastanın bilgileri güncellendi",
298
+ "bu hasta daha önce kayıt edilmemiş",
299
+ "hastanın adı ve yaşı kaydedildi",
300
+ "hasta bilgileri sisteme girildi",
301
+ "yeni hasta kaydı açıldı",
302
+ "hasta şikayeti sisteme işlendi",
303
+ "kayıt başarıyla güncellendi",
304
+ "hastanın geçmiş kayıtları bulundu",
305
+ "bu hasta için kayıt bulunamadı",
306
+ "hastanın durumu kaydedildi",
307
+ // ── Belirsizlik ve yönlendirme ──
308
+ "bu soruyu yanıtlayacak bilgiye sahip değilim",
309
+ "bu konuda bilgim yok başka bir soru sorabilirsiniz",
310
+ "lütfen daha fazla bilgi verir misiniz",
311
+ "hangi hastayı soruyorsunuz",
312
+ "bu bilgi hafızada bulunamadı",
313
+ "henüz bu konuda kayıt yok",
314
+ "bu işlemi yapabilmem için daha fazla bilgiye ihtiyacım var",
315
+ "maalesef bu konuda yardımcı olamıyorum",
316
+ // ── Sağlık domain kalıpları ──
317
+ "hastanın şikayeti baş ağrısı ve tansiyon yüksekliği",
318
+ "kan basıncı değeri yüz kırk bölü doksan olarak ölçüldü",
319
+ "ilaç dozu hesaplandı ve reçeteye yazıldı",
320
+ "risk durumu yüksek riskli olarak işaretlendi",
321
+ "hastanın kan şekeri yüz seksen olarak ölçüldü",
322
+ "diyabet hastası olarak kayıt edildi",
323
+ "gebelik takibi için kontrol randevusu oluşturuldu",
324
+ "hastanın ateşi otuz sekiz derece ölçüldü",
325
+ "tansiyon ölçümü yapıldı ve kaydedildi",
326
+ "ilaçlardan hangisinin verilmesi gerektiği belirlendi",
327
+ "doktorlarımızdan birini çağırabilir misiniz",
328
+ "hastanın ayaklarından birinde şişlik tespit edildi",
329
+ "kan grubu belirlenmesi için tahlil istendi",
330
+ "hastaya günde üç kez ilaç verilecek",
331
+ "haftalık toplam doz hesaplandı",
332
+ "ameliyat öncesi hazırlıklar tamamlandı",
333
+ "hastanın nabzı ve tansiyonu normal sınırlarda",
334
+ "tedavi planı oluşturuldu ve hastaya bildirildi",
335
+ "aşı takvimi kontrol edildi",
336
+ "acil müdahale gerekli değil hasta stabil",
337
+ // ── Çelişki ve doğrulama ──
338
+ "kayıtlarda çelişen bilgi tespit edildi",
339
+ "lütfen doğru bilgiyi belirtin",
340
+ "iki farklı kayıt bulundu hangisi doğru",
341
+ "bu bilgi önceki kayıtla çelişiyor",
342
+ "çelişen kayıtlar kullanıcıya sunuldu",
343
+ // ── Morfoloji zenginleştirme ──
344
+ "evlerimizden geliyoruz hasta getirdik",
345
+ "hastanın ayaklarından birinde şişlik var",
346
+ "ilaçlardan hangisini vermemiz gerekiyor",
347
+ "doktorlarımızdan birini çağırabilir misiniz",
348
+ "bu hastalıklardan kurtulabilir mi",
349
+ "köylerden gelen hastalar muayene edildi",
350
+ "çocukların aşıları yapıldı",
351
+ "hastaların kayıtları güncellendi"
352
+ ];
353
+
354
+ // Load external corpus roots
355
+ let initialVocab = [];
356
+ try {
357
+ if (this._runtime === 'browser') {
358
+ const url = options.corpusUrl || '../tr_corpus_embed.js';
359
+ const res = await fetch(url);
360
+ const txt = await res.text();
361
+ const m = txt.match(/const TR_CORPUS_ROOTS\s*=\s*(\[[\s\S]*?\]);/);
362
+ if (m) initialVocab = JSON.parse(m[1]);
363
+ } else {
364
+ const fs = await import('fs');
365
+ const path = await import('path');
366
+ const { fileURLToPath } = await import('url');
367
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
368
+ const corpusPath = options.corpusPath || path.join(__dirname, '..', 'tr_corpus_embed.js');
369
+ if (fs.existsSync(corpusPath)) {
370
+ const txt = fs.readFileSync(corpusPath, 'utf8');
371
+ const m = txt.match(/const TR_CORPUS_ROOTS\s*=\s*(\[[\s\S]*?\]);/);
372
+ if (m) initialVocab = JSON.parse(m[1]);
373
+ }
374
+ }
375
+ } catch (e) { /* fallback to embedded corpus */ }
376
+
377
+ const turkishRe = /^[a-zçgğıoöşuüâîû]+$/;
378
+ const words = new Set(['ev', 'yeni', 'bir', 'kitap', 'okul', 'çıktık', 'aldım', 'güzel',
379
+ 'iyi', 'yapay', 'zeka', 'bilimsel', 'okuma', 'öğrenmek', 'dil', 'türkçe', 'insan',
380
+ 'olmak', 'önemlidir', 'geliştirir', 'değiştiriyor', 'yürüdük', 'gittik', 'yola',
381
+ 'başlangıç', 'güne', 'uyandık', 'kuruludur', 'televizyonu', 'açıp', 'izledik',
382
+ 'izledim', 'arabaya', 'binip', 'bilgisayarı', 'kapatıp', 'uyudum', 'yattım']);
383
+
384
+ // Build word counts from corpus
385
+ this.wordCounts.clear();
386
+ for (const s of corpus) {
387
+ for (const w of s.split(/\s+/)) {
388
+ const c = w.trim().toLowerCase().replace(/[^a-zçgğıoöşuüâîû]/g, '');
389
+ if (c.length >= 2 && turkishRe.test(c)) {
390
+ this.wordCounts.set(c, (this.wordCounts.get(c) || 0) + 1);
391
+ }
392
+ }
393
+ }
394
+ for (const [w, cnt] of this.wordCounts) { if (cnt >= 2) words.add(w); }
395
+
396
+ // Morphological expansion: 5000 roots -> 50.000+ words
397
+ for (const w of initialVocab) {
398
+ const c = w.trim().toLowerCase().replace(/[^a-zçgğıoöşuüâîû]/g, '');
399
+ if (c.length >= 2 && turkishRe.test(c)) {
400
+ for (const d of generateDerivations(c, this.morphology)) words.add(d);
401
+ }
402
+ }
403
+
404
+ // Assemble vocabulary with <unk> safety token at index 0
405
+ this.vocab = ['<unk>', ...Array.from(words)];
406
+ this.vocabMap.clear();
407
+ for (let i = 0; i < this.vocab.length; i++) this.vocabMap.set(this.vocab[i], i);
408
+
409
+ // Precompute top-15 frequent word indices
410
+ this.freqIndices = this.vocab
411
+ .map((w, idx) => ({ w, idx }))
412
+ .sort((a, b) => (this.wordCounts.get(b.w) || 0) - (this.wordCounts.get(a.w) || 0))
413
+ .slice(0, 15).map(x => x.idx);
414
+
415
+ // Precompute derivation index map
416
+ this.wordDerivationIndices.clear();
417
+ for (const root of this.vocab) {
418
+ const indices = [];
419
+ for (const d of generateDerivations(root, this.morphology)) {
420
+ const idx = this.vocabMap.get(d);
421
+ if (idx !== undefined) indices.push(idx);
422
+ }
423
+ this.wordDerivationIndices.set(root, indices);
424
+ }
425
+
426
+ // Generate FHRR embeddings
427
+ for (const word of this.vocab) {
428
+ const emb = this.hdc.generateSeeded('complex', word);
429
+ emb.values = new Float32Array(emb.values);
430
+ const cos = new Float32Array(this.D);
431
+ const sin = new Float32Array(this.D);
432
+ for (let i = 0; i < this.D; i++) {
433
+ cos[i] = Math.cos(emb.values[i]);
434
+ sin[i] = Math.sin(emb.values[i]);
435
+ }
436
+ this.embeddings.set(word, { emb, cosVals: cos, sinVals: sin });
437
+ }
438
+
439
+ // Write to WASM memory
440
+ if (this.wasmInstance) {
441
+ const V = this.vocab.length, DD = this.D;
442
+ this.vocabCosPtr = this.wasmInstance.exports.malloc(V * DD * 4);
443
+ this.vocabSinPtr = this.wasmInstance.exports.malloc(V * DD * 4);
444
+ this.scoresPtr = this.wasmInstance.exports.malloc(V * 4);
445
+
446
+ this.transIndicesPtr = this.wasmInstance.exports.malloc(64 * 4);
447
+ this.transMultipliersPtr = this.wasmInstance.exports.malloc(64 * 4);
448
+ this.topIndicesPtr = this.wasmInstance.exports.malloc(5 * 4);
449
+ this.topScoresPtr = this.wasmInstance.exports.malloc(5 * 4);
450
+ this.candidateIndicesPtr = this.wasmInstance.exports.malloc(1024 * 4);
451
+
452
+ if (this.vocabCosPtr && this.vocabSinPtr && this.scoresPtr &&
453
+ this.transIndicesPtr && this.transMultipliersPtr &&
454
+ this.topIndicesPtr && this.topScoresPtr && this.candidateIndicesPtr) {
455
+ const mem = this.wasmMemory.buffer;
456
+ const cosView = new Float32Array(mem, this.vocabCosPtr, V * DD);
457
+ const sinView = new Float32Array(mem, this.vocabSinPtr, V * DD);
458
+ for (let v = 0; v < V; v++) {
459
+ const data = this.embeddings.get(this.vocab[v]);
460
+ cosView.set(data.cosVals, v * DD);
461
+ sinView.set(data.sinVals, v * DD);
462
+ }
463
+ } else {
464
+ console.warn('ResonanceSDK: WASM malloc failed, falling back to Pure JS.');
465
+ this.wasmInstance = null;
466
+ }
467
+ }
468
+
469
+ // Build bi-gram transition model
470
+ this.transitionCounts = new Map();
471
+ const re = new Float32Array(this.D), im = new Float32Array(this.D);
472
+ let tCount = 0;
473
+ for (const s of corpus) {
474
+ const ws = s.split(/\s+/).map(w => w.trim().toLowerCase().replace(/[^a-zçgğıoöşuüâîû]/g, '')).filter(w => w.length >= 2);
475
+ for (let i = 0; i < ws.length - 1; i++) {
476
+ const w1 = ws[i], w2 = ws[i + 1];
477
+ if (!this.transitionCounts.has(w1)) this.transitionCounts.set(w1, new Map());
478
+ this.transitionCounts.get(w1).set(w2, (this.transitionCounts.get(w1).get(w2) || 0) + 1);
479
+
480
+ const d1 = this.embeddings.get(w1), d2 = this.embeddings.get(w2);
481
+ if (d1 && d2) {
482
+ const wt = 1.0 / Math.sqrt(this.wordCounts.get(w2) || 1);
483
+ for (let j = 0; j < this.D; j++) {
484
+ let diff = d2.emb.values[j] - d1.emb.values[j];
485
+ if (diff < 0) diff += 2 * Math.PI;
486
+ re[j] += wt * Math.cos(diff % (2 * Math.PI));
487
+ im[j] += wt * Math.sin(diff % (2 * Math.PI));
488
+ }
489
+ tCount++;
490
+ }
491
+ }
492
+ }
493
+
494
+ // Bundle transition phase differences
495
+ if (tCount > 0) {
496
+ const vals = new Float32Array(this.D);
497
+ for (let j = 0; j < this.D; j++) {
498
+ let v = Math.atan2(im[j], re[j]);
499
+ if (v < 0) v += 2 * Math.PI;
500
+ vals[j] = v;
501
+ }
502
+ this.sparseWeightsSpec = new Representation('complex', vals, this.D);
503
+ } else {
504
+ this.sparseWeightsSpec = this.hdc.generateSeeded('complex', 'fallback_transitions');
505
+ }
506
+
507
+ // Write transition spectrum to WASM
508
+ if (this.wasmInstance && this.sparseWeightsSpec) {
509
+ const mem = this.wasmMemory.buffer;
510
+ const trRe = new Float32Array(mem, this.wasmInstance.exports.get_transition_spec_re_ptr(), this.D);
511
+ const trIm = new Float32Array(mem, this.wasmInstance.exports.get_transition_spec_im_ptr(), this.D);
512
+ for (let j = 0; j < this.D; j++) {
513
+ trRe[j] = Math.cos(this.sparseWeightsSpec.values[j]);
514
+ trIm[j] = Math.sin(this.sparseWeightsSpec.values[j]);
515
+ }
516
+ }
517
+ }
518
+
519
+ _predictNext(tokens, temperature, promptLength) {
520
+ if (this.wasmInstance && this.wasmMemory) {
521
+ return this._predictWasm(tokens, temperature, promptLength);
522
+ }
523
+ return this._predictJS(tokens, temperature, promptLength);
524
+ }
525
+
526
+ _predictWasm(tokens, temperature, promptLength) {
527
+ const mem = this.wasmMemory.buffer;
528
+ const ctxPtr = this.wasmInstance.exports.get_context_angles_ptr();
529
+ const ctxView = new Float32Array(mem, ctxPtr, this.D);
530
+
531
+ // 1. Context combination zero-copy
532
+ if (promptLength > 0 && tokens.length > promptLength) {
533
+ let pIdx = this.vocabMap.get(tokens[promptLength - 1]) ?? 0;
534
+ let gIdx = this.vocabMap.get(tokens[tokens.length - 1]) ?? 0;
535
+ this.wasmInstance.exports.combine_phases_by_indices(
536
+ pIdx, 0.4, gIdx, 0.6, this.vocabCosPtr, this.vocabSinPtr, ctxPtr
537
+ );
538
+ } else {
539
+ const lastData = this.embeddings.get(tokens[tokens.length - 1]) || this.embeddings.get('<unk>');
540
+ ctxView.set(lastData.emb.values);
541
+ }
542
+
543
+ // 2. Forward FFT & Spectral prediction
544
+ this.wasmInstance.exports.spectral_predict();
545
+
546
+ // 3. Candidate building
547
+ const candView = new Int32Array(mem, this.candidateIndicesPtr, 1024);
548
+ const freqLen = this.freqIndices.length;
549
+ for (let i = 0; i < freqLen; i++) {
550
+ candView[i] = this.freqIndices[i];
551
+ }
552
+ let numCandidates = freqLen;
553
+
554
+ for (let i = 0; i < promptLength; i++) {
555
+ const idx = this.vocabMap.get(tokens[i]);
556
+ if (idx !== undefined && numCandidates < 1024) {
557
+ candView[numCandidates++] = idx;
558
+ }
559
+ }
560
+
561
+ const lastToken = tokens[tokens.length - 1];
562
+ let numTransitions = -1;
563
+
564
+ if (lastToken) {
565
+ const m = this.transitionCounts.get(lastToken);
566
+ if (!m) {
567
+ numTransitions = 0;
568
+ } else {
569
+ const transIndices = new Int32Array(mem, this.transIndicesPtr, m.size);
570
+ const transMultipliers = new Float32Array(mem, this.transMultipliersPtr, m.size);
571
+
572
+ let idx = 0;
573
+ for (const [nextWord, count] of m.entries()) {
574
+ const wordIdx = this.vocabMap.get(nextWord);
575
+ if (wordIdx !== undefined) {
576
+ transIndices[idx] = wordIdx;
577
+ transMultipliers[idx] = 1.5 + count * 2.0;
578
+ idx++;
579
+
580
+ if (numCandidates < 1024) {
581
+ candView[numCandidates++] = wordIdx;
582
+ }
583
+
584
+ const derivationsIndices = this.wordDerivationIndices.get(nextWord);
585
+ if (derivationsIndices) {
586
+ const derivLen = derivationsIndices.length;
587
+ for (let d = 0; d < derivLen; d++) {
588
+ if (numCandidates < 1024) {
589
+ candView[numCandidates++] = derivationsIndices[d];
590
+ }
591
+ }
592
+ }
593
+ }
594
+ }
595
+ numTransitions = idx;
596
+ }
597
+ }
598
+
599
+ // 4. WASM cosine similarity for candidates
600
+ this.wasmInstance.exports.compute_similarity_for_candidates(
601
+ this.candidateIndicesPtr,
602
+ numCandidates,
603
+ this.vocabCosPtr,
604
+ this.vocabSinPtr,
605
+ this.transIndicesPtr,
606
+ this.transMultipliersPtr,
607
+ numTransitions,
608
+ this.topIndicesPtr,
609
+ this.topScoresPtr
610
+ );
611
+
612
+ // 5. Read top-5 and apply penalties
613
+ const topIndicesView = new Int32Array(mem, this.topIndicesPtr, 5);
614
+ const topScoresView = new Float32Array(mem, this.topScoresPtr, 5);
615
+
616
+ const candidates = [];
617
+ const lastHarmony = lastToken ? this.morphology.determineVowelHarmony(lastToken) : null;
618
+ const recentK = tokens.slice(-4);
619
+
620
+ for (let i = 0; i < 5; i++) {
621
+ const wordIdx = topIndicesView[i];
622
+ if (wordIdx === -1) continue;
623
+
624
+ const word = this.vocab[wordIdx];
625
+ let score = topScoresView[i];
626
+
627
+ if (tokens.length >= 1) {
628
+ const last1 = tokens[tokens.length - 1];
629
+ for (let idx = 0; idx < tokens.length - 1; idx++) {
630
+ if (tokens[idx] === last1 && tokens[idx + 1] === word) { score = 0.0; break; }
631
+ }
632
+ }
633
+ if (tokens.length >= 2) {
634
+ const last2 = tokens[tokens.length - 2];
635
+ const last1 = tokens[tokens.length - 1];
636
+ for (let idx = 0; idx < tokens.length - 2; idx++) {
637
+ if (tokens[idx] === last2 && tokens[idx + 1] === last1 && tokens[idx + 2] === word) { score = 0.0; break; }
638
+ }
639
+ }
640
+ if (recentK.includes(word)) {
641
+ score *= 0.1;
642
+ }
643
+ if (lastHarmony && this.morphology.suffixFeatures.hasOwnProperty(word)) {
644
+ const suffixHarmony = this.morphology.determineVowelHarmony(word);
645
+ if (suffixHarmony !== lastHarmony) score = 0.0;
646
+ } else if (lastHarmony) {
647
+ const candidateHarmony = this.morphology.determineVowelHarmony(word);
648
+ if (candidateHarmony === lastHarmony) score *= 1.15;
649
+ }
650
+
651
+ candidates.push({ word, score });
652
+ }
653
+
654
+ candidates.sort((a, b) => b.score - a.score);
655
+ return this._sample(candidates, temperature);
656
+ }
657
+
658
+ _predictJS(tokens, temperature, promptLength) {
659
+ const ctx = new Float32Array(this.D);
660
+ if (promptLength > 0 && tokens.length > promptLength) {
661
+ const pD = this.embeddings.get(tokens[promptLength - 1]) || this.embeddings.get('<unk>');
662
+ const gD = this.embeddings.get(tokens[tokens.length - 1]) || this.embeddings.get('<unk>');
663
+ for (let j = 0; j < this.D; j++) {
664
+ let v = pD.emb.values[j] + gD.emb.values[j];
665
+ if (v < 0) v += 2 * Math.PI;
666
+ ctx[j] = v % (2 * Math.PI);
667
+ }
668
+ } else {
669
+ const ld = this.embeddings.get(tokens[tokens.length - 1]) || this.embeddings.get('<unk>');
670
+ ctx.set(ld.emb.values);
671
+ }
672
+
673
+ const query = new Float32Array(this.D);
674
+ for (let j = 0; j < this.D; j++) {
675
+ let v = ctx[j] + (this.sparseWeightsSpec ? this.sparseWeightsSpec.values[j] : 0);
676
+ if (v < 0) v += 2 * Math.PI;
677
+ query[j] = v % (2 * Math.PI);
678
+ }
679
+
680
+ // Scan all vocab in JS fallback (matches core behavior)
681
+ const cosY = new Float32Array(this.D);
682
+ const sinY = new Float32Array(this.D);
683
+ for (let i = 0; i < this.D; i++) {
684
+ cosY[i] = Math.cos(query[i]);
685
+ sinY[i] = Math.sin(query[i]);
686
+ }
687
+
688
+ const candidates = [];
689
+ const lastToken = tokens[tokens.length - 1];
690
+ const lastHarmony = lastToken ? this.morphology.determineVowelHarmony(lastToken) : null;
691
+ const recentK = tokens.slice(-4);
692
+
693
+ for (const [word, data] of this.embeddings.entries()) {
694
+ let sumCos = 0.0;
695
+ const cosEmb = data.cosVals;
696
+ const sinEmb = data.sinVals;
697
+ for (let i = 0; i < this.D; i++) {
698
+ sumCos += cosY[i] * cosEmb[i] + sinY[i] * sinEmb[i];
699
+ }
700
+ let score = sumCos / this.D;
701
+ score = Math.max(0.0001, (score + 1.0) / 2.0);
702
+
703
+ if (lastToken) {
704
+ const m = this.transitionCounts.get(lastToken);
705
+ if (m) {
706
+ const count = m.get(word) || 0;
707
+ score *= (count > 0 ? (1.5 + count * 2.0) : 0.1);
708
+ } else {
709
+ score *= 0.5;
710
+ }
711
+ }
712
+
713
+ if (tokens.length >= 1) {
714
+ const last1 = tokens[tokens.length - 1];
715
+ for (let i = 0; i < tokens.length - 1; i++) {
716
+ if (tokens[i] === last1 && tokens[i + 1] === word) { score = 0.0; break; }
717
+ }
718
+ }
719
+ if (tokens.length >= 2) {
720
+ const last2 = tokens[tokens.length - 2];
721
+ const last1 = tokens[tokens.length - 1];
722
+ for (let i = 0; i < tokens.length - 2; i++) {
723
+ if (tokens[i] === last2 && tokens[i + 1] === last1 && tokens[i + 2] === word) { score = 0.0; break; }
724
+ }
725
+ }
726
+ if (recentK.includes(word)) {
727
+ score *= 0.1;
728
+ }
729
+ if (lastHarmony && this.morphology.suffixFeatures.hasOwnProperty(word)) {
730
+ const suffixHarmony = this.morphology.determineVowelHarmony(word);
731
+ if (suffixHarmony !== lastHarmony) score = 0.0;
732
+ } else if (lastHarmony) {
733
+ const candidateHarmony = this.morphology.determineVowelHarmony(word);
734
+ if (candidateHarmony === lastHarmony) score *= 1.15;
735
+ }
736
+
737
+ candidates.push({ word, score });
738
+ }
739
+
740
+ candidates.sort((a, b) => b.score - a.score);
741
+ return this._sample(candidates.slice(0, 5), temperature);
742
+ }
743
+
744
+ _sample(candidates, temperature) {
745
+ if (!candidates.length) return { word: '<unk>', score: 0 };
746
+ if (temperature <= 0.05) return candidates[0]; // Greedy
747
+
748
+ const max = candidates[0].score;
749
+ const exp = candidates.map(c => Math.exp((c.score - max) / temperature));
750
+ const sum = exp.reduce((a, v) => a + v, 0);
751
+ const probs = exp.map(v => v / sum);
752
+ const r = Math.random();
753
+ let cum = 0;
754
+ for (let i = 0; i < candidates.length; i++) {
755
+ cum += probs[i];
756
+ if (r <= cum) return candidates[i];
757
+ }
758
+ return candidates[0];
759
+ }
760
+ _isTerminal(word) {
761
+ const terminals = new Set(['.', 'gittik', 'aldım', 'aldık', 'geliştirir', 'uyandık',
762
+ 'kuruludur', 'çıktık', 'izledim', 'izledik', 'uyudum', 'yattım', 'gittim', 'yaptım', 'öğrendim']);
763
+ return terminals.has(word) || word.endsWith('.');
764
+ }
765
+
766
+ _assertInit() {
767
+ if (!this._initialized) throw new Error('ResonanceSDK: Not initialized. Call await sdk.init() first.');
768
+ }
769
+
770
+ /**
771
+ * Factory to create an isolated GHR-Lattice memory engine instance.
772
+ * @param {Object} [config]
773
+ * @returns {GHRLatticeMemory}
774
+ */
775
+ createLatticeMemory(config = {}) {
776
+ return new GHRLatticeMemory({
777
+ D: this.D,
778
+ ...config
779
+ });
780
+ }
781
+ }
782
+
783
+ export {
784
+ TurkishMorphology,
785
+ HDCEngine,
786
+ Representation,
787
+ MemoryEngine,
788
+ ReasoningRouter,
789
+ SovereignAgent,
790
+ SpectralEngine,
791
+ GHRLatticeMemory
792
+ };