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,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
|
+
};
|