@astermind/astermind-pro
Version:
Astermind Pro - Premium ML Toolkit with Advanced RAG, Reranking, Summarization, and Information Flow Analysis
120 lines • 4.35 kB
JavaScript
// Model serialization utilities
// Extracted from workers for reuse
import { requireLicense } from '../core/license.js';
/**
* Small, deterministic hash (not cryptographic)
*/
export function quickHash(s) {
let h1 = 0x9e3779b1, h2 = 0x85ebca6b;
for (let i = 0; i < s.length; i++) {
const c = s.charCodeAt(i);
h1 = Math.imul(h1 ^ c, 0x85ebca6b);
h2 = Math.imul(h2 ^ c, 0xc2b2ae35);
}
h1 = (h1 ^ (h2 >>> 15)) >>> 0;
return ('00000000' + h1.toString(16)).slice(-8);
}
/**
* Export model to serialized format
*/
export function exportModel(opts) {
requireLicense(); // Premium feature - requires valid license
const { settings, vocabMap, idf, chunks, tfidfDocs, landmarksIdx, landmarkMat, denseDocs, includeRich = true, includeDense = false, } = opts;
// 1) settings snapshot (clone to avoid accidental mutation)
const settingsSnap = JSON.parse(JSON.stringify(settings || {}));
// 2) vocab
const vocab = Array.from(vocabMap.entries());
// 3) chunks (minimal text)
const chunksSnap = chunks.map(c => ({
heading: c.heading,
content: c.content || '',
rich: includeRich ? (c.rich || undefined) : undefined,
level: c.level,
secId: c.secId,
}));
// 4) tfidfDocs → array of pairs
const tfidfPairs = tfidfDocs.map((m) => {
const row = [];
for (const [i, v] of m)
row.push([i, v]);
// sort indices for determinism
row.sort((a, b) => a[0] - b[0]);
return row;
});
// 5) Nyström landmarks and (optional) denseDocs
const landmarkMatArr = landmarkMat.map(v => Array.from(v));
const denseDocsArr = includeDense ?
(denseDocs?.map(v => Array.from(v)) || undefined) : undefined;
const payload = {
version: 'astermind-pro-v1',
savedAt: new Date().toISOString(),
settings: settingsSnap,
vocab,
idf: Array.from(idf),
chunks: chunksSnap,
tfidfDocs: tfidfPairs,
landmarksIdx: Array.from(landmarksIdx),
landmarkMat: landmarkMatArr,
denseDocs: denseDocsArr,
};
// (Optional) quick content hash for sanity (small & deterministic)
payload.hash = quickHash(JSON.stringify({
idf: payload.idf.slice(0, 64),
vi: payload.vocab.length,
ci: payload.chunks.length,
lm: payload.landmarksIdx.length
}));
return payload;
}
export function importModel(model, opts) {
requireLicense(); // Premium feature - requires valid license
if (model.version !== 'astermind-pro-v1' && model.version !== 'astermind-elm-v1') {
throw new Error(`Unsupported model version: ${model.version}. Expected 'astermind-pro-v1' or 'astermind-elm-v1'`);
}
// 1) restore settings
const settings = JSON.parse(JSON.stringify(model.settings || {}));
// 2) vocab & idf
const vocabMap = new Map(model.vocab);
const idf = Float64Array.from(model.idf); // keep as number[] for compatibility
// 3) chunks
const chunks = model.chunks.map(c => ({
heading: c.heading,
content: c.content || '',
rich: c.rich,
level: c.level,
secId: c.secId
}));
// 4) tfidfDocs from pairs
const tfidfDocs = model.tfidfDocs.map(row => {
const m = new Map();
for (const [i, v] of row)
m.set(i, v);
return m;
});
// 5) Nyström landmarks
const landmarksIdx = Array.from(model.landmarksIdx);
const landmarkMat = model.landmarkMat.map(a => Float64Array.from(a));
// 6) denseDocs: use stored or recompute
const needRecompute = (opts?.recomputeDense === true) || !model.denseDocs || model.denseDocs.length !== tfidfDocs.length;
let denseDocs;
if (needRecompute && opts?.buildDense) {
denseDocs = opts.buildDense(tfidfDocs, vocabMap.size, landmarkMat, settings.kernel || 'rbf', settings.sigma || 1.0);
}
else if (needRecompute) {
throw new Error('recomputeDense=true but buildDense function not provided');
}
else {
denseDocs = model.denseDocs.map(a => Float64Array.from(a));
}
return {
settings,
vocabMap,
idf,
chunks,
tfidfDocs,
landmarksIdx,
landmarkMat,
denseDocs,
};
}
//# sourceMappingURL=model-serialization.js.map