@moolam/edge-agent 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 +202 -0
- package/README.md +54 -0
- package/dist/cognitive_bindings.d.ts +329 -0
- package/dist/cognitive_bindings.d.ts.map +1 -0
- package/dist/cognitive_bindings.js +1293 -0
- package/dist/cognitive_bindings.js.map +1 -0
- package/dist/coldstart.d.ts +95 -0
- package/dist/coldstart.d.ts.map +1 -0
- package/dist/coldstart.js +224 -0
- package/dist/coldstart.js.map +1 -0
- package/dist/edge_agent.d.ts +216 -0
- package/dist/edge_agent.d.ts.map +1 -0
- package/dist/edge_agent.js +530 -0
- package/dist/edge_agent.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/local_vector_db.d.ts +115 -0
- package/dist/local_vector_db.d.ts.map +1 -0
- package/dist/local_vector_db.js +297 -0
- package/dist/local_vector_db.js.map +1 -0
- package/dist/slm_runtime.d.ts +158 -0
- package/dist/slm_runtime.d.ts.map +1 -0
- package/dist/slm_runtime.js +247 -0
- package/dist/slm_runtime.js.map +1 -0
- package/dist/trajectory_capture.d.ts +83 -0
- package/dist/trajectory_capture.d.ts.map +1 -0
- package/dist/trajectory_capture.js +193 -0
- package/dist/trajectory_capture.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module local_vector_db
|
|
3
|
+
*
|
|
4
|
+
* Offline vector memory of the Edge Harness — the on-device mirror of the
|
|
5
|
+
* cloud Memory & Context Engine (MCE).
|
|
6
|
+
*
|
|
7
|
+
* Storage strategy: a `StorageDriver` abstraction over SQLite so the same
|
|
8
|
+
* logic runs on expo-sqlite (mobile), better-sqlite3 (desktop/CI), and
|
|
9
|
+
* wa-sqlite/OPFS (web). Vectors are packed Float32 BLOBs; similarity search
|
|
10
|
+
* is brute-force cosine over a working set bounded by `maxResidentVectors`.
|
|
11
|
+
*
|
|
12
|
+
* `semantic` kind, `deleteById`, query-dimension hard error,
|
|
13
|
+
* injectable `nowMs` for decay probes.
|
|
14
|
+
* Kind-aware decay factor + correction pinning under scan
|
|
15
|
+
* caps; recall ordering prefers pinned kinds when scores tie.
|
|
16
|
+
*/
|
|
17
|
+
import type { StorageDriver } from "@moolam/contracts";
|
|
18
|
+
import type { ConceptId, HLCTimestamp } from "@moolam/sync-protocol";
|
|
19
|
+
export type { StorageDriver };
|
|
20
|
+
/** Memory class shared with MemoryInterface kinds (incl. semantic). */
|
|
21
|
+
export type VectorMemoryKind = "correction" | "milestone" | "preference" | "episodic" | "semantic";
|
|
22
|
+
/** A single retrievable memory: a moment worth remembering about the subject. */
|
|
23
|
+
export interface MemoryRecord {
|
|
24
|
+
/** ULID-style unique id, generated on-device. */
|
|
25
|
+
id: string;
|
|
26
|
+
subjectId: string;
|
|
27
|
+
conceptId: ConceptId;
|
|
28
|
+
/** Natural-language content, e.g. "confused derivative with slope of chord". */
|
|
29
|
+
text: string;
|
|
30
|
+
/** Embedding produced by the active SlmRuntime. Dimension is store-wide. */
|
|
31
|
+
vector: Float32Array;
|
|
32
|
+
/** Memory class drives decay: corrections/semantic never decay; episodics do. */
|
|
33
|
+
kind: VectorMemoryKind;
|
|
34
|
+
createdAt: HLCTimestamp;
|
|
35
|
+
}
|
|
36
|
+
export interface ScoredMemory {
|
|
37
|
+
record: MemoryRecord;
|
|
38
|
+
/** Cosine similarity in [-1, 1], already decay-weighted. */
|
|
39
|
+
score: number;
|
|
40
|
+
}
|
|
41
|
+
export type LocalVectorDbOptions = {
|
|
42
|
+
/** Hard cap on rows scanned per query; oldest episodics evicted first. */
|
|
43
|
+
maxResidentVectors?: number;
|
|
44
|
+
/** Half-life (days) applied to episodic memories during scoring. */
|
|
45
|
+
episodicHalfLifeDays?: number;
|
|
46
|
+
/** Injected clock for decay (MEMOADAP / CK-02.2). Defaults to Date.now. */
|
|
47
|
+
nowMs?: () => number;
|
|
48
|
+
};
|
|
49
|
+
/** MCE-03 / CK-02.2 default episodic half-life (30 days). */
|
|
50
|
+
export declare const EPISODIC_HALF_LIFE_DAYS = 30;
|
|
51
|
+
export declare const EPISODIC_HALF_LIFE_MS: number;
|
|
52
|
+
/** Accept ISO-8601 or HLC physical prefix for age computation. */
|
|
53
|
+
export declare function parseMemoryCreatedAtMs(createdAt: string): number;
|
|
54
|
+
/**
|
|
55
|
+
* Kind-aware decay (CK-02.2): only `episodic` decays; correction /
|
|
56
|
+
* milestone / preference / semantic stay at 1.0 (never forget corrections).
|
|
57
|
+
*/
|
|
58
|
+
export declare function kindAwareDecayFactor(kind: VectorMemoryKind, createdAt: string, nowMs: number, halfLifeMs?: number): number;
|
|
59
|
+
/**
|
|
60
|
+
* Pin priority for tie-breaks and working-set retention (lower = stronger).
|
|
61
|
+
* Corrections are pinned above all other kinds.
|
|
62
|
+
*/
|
|
63
|
+
export declare function memoryKindPinRank(kind: VectorMemoryKind): number;
|
|
64
|
+
/**
|
|
65
|
+
* Working-set selection under `maxResidentVectors`: keep every correction,
|
|
66
|
+
* then fill remaining slots (oldest episodics drop first when over cap).
|
|
67
|
+
*/
|
|
68
|
+
export declare function pinCorrectionsInWorkingSet<T extends {
|
|
69
|
+
kind: VectorMemoryKind;
|
|
70
|
+
created_at: string;
|
|
71
|
+
}>(rows: readonly T[], cap: number): T[];
|
|
72
|
+
/** Score desc, then pin-rank asc (correction before episodic on ties). */
|
|
73
|
+
export declare function compareScoredMemories(a: ScoredMemory, b: ScoredMemory): number;
|
|
74
|
+
/**
|
|
75
|
+
* Brute-force-exact, decay-aware local vector store.
|
|
76
|
+
*
|
|
77
|
+
* All writes are durable before `upsert` resolves; the friction telemetry
|
|
78
|
+
* and sync layers rely on this store never losing an acknowledged record.
|
|
79
|
+
*/
|
|
80
|
+
export declare class LocalVectorDb {
|
|
81
|
+
private readonly driver;
|
|
82
|
+
private readonly options;
|
|
83
|
+
private dimension;
|
|
84
|
+
constructor(driver: StorageDriver, options?: LocalVectorDbOptions);
|
|
85
|
+
/** Locked embedding width after first upsert; null until then. */
|
|
86
|
+
get embeddingDimension(): number | null;
|
|
87
|
+
/** Create tables/indexes. Safe to call on every app launch. */
|
|
88
|
+
initialize(): Promise<void>;
|
|
89
|
+
/** Insert or replace a memory. Dimension is locked by the first write. */
|
|
90
|
+
upsert(record: MemoryRecord): Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* Retrieve the memories most relevant to `queryVector`, optionally scoped
|
|
93
|
+
* to a concept. Scores are cosine × {@link kindAwareDecayFactor}; corrections
|
|
94
|
+
* never decay and are pinned in the working set under scan caps.
|
|
95
|
+
*/
|
|
96
|
+
search(subjectId: string, queryVector: Float32Array, opts?: {
|
|
97
|
+
conceptId?: ConceptId;
|
|
98
|
+
limit?: number;
|
|
99
|
+
}): Promise<ScoredMemory[]>;
|
|
100
|
+
/** Delete one row by id (MemoryInterface.forget). */
|
|
101
|
+
deleteById(id: string): Promise<void>;
|
|
102
|
+
/**
|
|
103
|
+
* Remove episodic records past retention. When `subjectId` is set, only that
|
|
104
|
+
* subject's rows are pruned (subject isolation).
|
|
105
|
+
*/
|
|
106
|
+
pruneEpisodicOlderThan(cutoff: HLCTimestamp, subjectId?: string): Promise<number>;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Durable in-memory StorageDriver for the SQL LocalVectorDb issues.
|
|
110
|
+
* execute flushes rows before resolve (CK-02.1 substrate).
|
|
111
|
+
*/
|
|
112
|
+
export declare function createLocalVectorMemoryDriver(): StorageDriver & {
|
|
113
|
+
rowCount(): number;
|
|
114
|
+
};
|
|
115
|
+
//# sourceMappingURL=local_vector_db.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local_vector_db.d.ts","sourceRoot":"","sources":["../src/local_vector_db.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module local_vector_db
|
|
3
|
+
*
|
|
4
|
+
* Offline vector memory of the Edge Harness — the on-device mirror of the
|
|
5
|
+
* cloud Memory & Context Engine (MCE).
|
|
6
|
+
*
|
|
7
|
+
* Storage strategy: a `StorageDriver` abstraction over SQLite so the same
|
|
8
|
+
* logic runs on expo-sqlite (mobile), better-sqlite3 (desktop/CI), and
|
|
9
|
+
* wa-sqlite/OPFS (web). Vectors are packed Float32 BLOBs; similarity search
|
|
10
|
+
* is brute-force cosine over a working set bounded by `maxResidentVectors`.
|
|
11
|
+
*
|
|
12
|
+
* `semantic` kind, `deleteById`, query-dimension hard error,
|
|
13
|
+
* injectable `nowMs` for decay probes.
|
|
14
|
+
* Kind-aware decay factor + correction pinning under scan
|
|
15
|
+
* caps; recall ordering prefers pinned kinds when scores tie.
|
|
16
|
+
*/
|
|
17
|
+
const SCHEMA_SQL = `
|
|
18
|
+
CREATE TABLE IF NOT EXISTS memory_records (
|
|
19
|
+
id TEXT PRIMARY KEY,
|
|
20
|
+
subject_id TEXT NOT NULL,
|
|
21
|
+
concept_id TEXT NOT NULL,
|
|
22
|
+
text TEXT NOT NULL,
|
|
23
|
+
vector BLOB NOT NULL,
|
|
24
|
+
kind TEXT NOT NULL CHECK (kind IN ('correction','milestone','preference','episodic','semantic')),
|
|
25
|
+
created_at TEXT NOT NULL
|
|
26
|
+
);
|
|
27
|
+
CREATE INDEX IF NOT EXISTS idx_memory_subject_concept
|
|
28
|
+
ON memory_records (subject_id, concept_id);
|
|
29
|
+
`;
|
|
30
|
+
/** MCE-03 / CK-02.2 default episodic half-life (30 days). */
|
|
31
|
+
export const EPISODIC_HALF_LIFE_DAYS = 30;
|
|
32
|
+
export const EPISODIC_HALF_LIFE_MS = EPISODIC_HALF_LIFE_DAYS * 86_400_000;
|
|
33
|
+
/** Accept ISO-8601 or HLC physical prefix for age computation. */
|
|
34
|
+
export function parseMemoryCreatedAtMs(createdAt) {
|
|
35
|
+
const iso = Date.parse(createdAt);
|
|
36
|
+
if (!Number.isNaN(iso))
|
|
37
|
+
return iso;
|
|
38
|
+
const physical = Number(createdAt.slice(0, 15));
|
|
39
|
+
if (!Number.isFinite(physical)) {
|
|
40
|
+
throw new Error(`unparseable createdAt: ${createdAt}`);
|
|
41
|
+
}
|
|
42
|
+
return physical;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Kind-aware decay (CK-02.2): only `episodic` decays; correction /
|
|
46
|
+
* milestone / preference / semantic stay at 1.0 (never forget corrections).
|
|
47
|
+
*/
|
|
48
|
+
export function kindAwareDecayFactor(kind, createdAt, nowMs, halfLifeMs = EPISODIC_HALF_LIFE_MS) {
|
|
49
|
+
if (kind !== "episodic")
|
|
50
|
+
return 1;
|
|
51
|
+
const ageMs = Math.max(0, nowMs - parseMemoryCreatedAtMs(createdAt));
|
|
52
|
+
if (ageMs === 0)
|
|
53
|
+
return 1;
|
|
54
|
+
return Math.exp((-Math.LN2 * ageMs) / halfLifeMs);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Pin priority for tie-breaks and working-set retention (lower = stronger).
|
|
58
|
+
* Corrections are pinned above all other kinds.
|
|
59
|
+
*/
|
|
60
|
+
export function memoryKindPinRank(kind) {
|
|
61
|
+
switch (kind) {
|
|
62
|
+
case "correction":
|
|
63
|
+
return 0;
|
|
64
|
+
case "milestone":
|
|
65
|
+
return 1;
|
|
66
|
+
case "preference":
|
|
67
|
+
return 2;
|
|
68
|
+
case "semantic":
|
|
69
|
+
return 3;
|
|
70
|
+
case "episodic":
|
|
71
|
+
return 4;
|
|
72
|
+
default:
|
|
73
|
+
return 5;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Working-set selection under `maxResidentVectors`: keep every correction,
|
|
78
|
+
* then fill remaining slots (oldest episodics drop first when over cap).
|
|
79
|
+
*/
|
|
80
|
+
export function pinCorrectionsInWorkingSet(rows, cap) {
|
|
81
|
+
if (rows.length <= cap)
|
|
82
|
+
return [...rows];
|
|
83
|
+
const corrections = rows.filter((r) => r.kind === "correction");
|
|
84
|
+
const others = rows
|
|
85
|
+
.filter((r) => r.kind !== "correction")
|
|
86
|
+
.sort((a, b) => {
|
|
87
|
+
// Prefer newer non-corrections when space is scarce.
|
|
88
|
+
return b.created_at.localeCompare(a.created_at);
|
|
89
|
+
});
|
|
90
|
+
if (corrections.length >= cap) {
|
|
91
|
+
// Still never drop a correction below another correction by age — keep all
|
|
92
|
+
// corrections even if over cap (pin invariant beats soft NFR scan cap).
|
|
93
|
+
return corrections;
|
|
94
|
+
}
|
|
95
|
+
const room = cap - corrections.length;
|
|
96
|
+
return [...corrections, ...others.slice(0, room)];
|
|
97
|
+
}
|
|
98
|
+
/** Score desc, then pin-rank asc (correction before episodic on ties). */
|
|
99
|
+
export function compareScoredMemories(a, b) {
|
|
100
|
+
if (b.score !== a.score)
|
|
101
|
+
return b.score - a.score;
|
|
102
|
+
return (memoryKindPinRank(a.record.kind) - memoryKindPinRank(b.record.kind));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Brute-force-exact, decay-aware local vector store.
|
|
106
|
+
*
|
|
107
|
+
* All writes are durable before `upsert` resolves; the friction telemetry
|
|
108
|
+
* and sync layers rely on this store never losing an acknowledged record.
|
|
109
|
+
*/
|
|
110
|
+
export class LocalVectorDb {
|
|
111
|
+
driver;
|
|
112
|
+
options;
|
|
113
|
+
dimension = null;
|
|
114
|
+
constructor(driver, options = {}) {
|
|
115
|
+
this.driver = driver;
|
|
116
|
+
this.options = options;
|
|
117
|
+
}
|
|
118
|
+
/** Locked embedding width after first upsert; null until then. */
|
|
119
|
+
get embeddingDimension() {
|
|
120
|
+
return this.dimension;
|
|
121
|
+
}
|
|
122
|
+
/** Create tables/indexes. Safe to call on every app launch. */
|
|
123
|
+
async initialize() {
|
|
124
|
+
for (const statement of SCHEMA_SQL.split(";")
|
|
125
|
+
.map((s) => s.trim())
|
|
126
|
+
.filter(Boolean)) {
|
|
127
|
+
await this.driver.execute(statement);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/** Insert or replace a memory. Dimension is locked by the first write. */
|
|
131
|
+
async upsert(record) {
|
|
132
|
+
if (this.dimension === null)
|
|
133
|
+
this.dimension = record.vector.length;
|
|
134
|
+
if (record.vector.length !== this.dimension) {
|
|
135
|
+
throw new Error(`vector dimension ${record.vector.length} does not match store dimension ${this.dimension}; ` +
|
|
136
|
+
"re-embed the corpus before switching embedding models");
|
|
137
|
+
}
|
|
138
|
+
await this.driver.execute(`INSERT OR REPLACE INTO memory_records (id, subject_id, concept_id, text, vector, kind, created_at)
|
|
139
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`, [
|
|
140
|
+
record.id,
|
|
141
|
+
record.subjectId,
|
|
142
|
+
record.conceptId,
|
|
143
|
+
record.text,
|
|
144
|
+
new Uint8Array(record.vector.buffer.slice(0)),
|
|
145
|
+
record.kind,
|
|
146
|
+
record.createdAt,
|
|
147
|
+
]);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Retrieve the memories most relevant to `queryVector`, optionally scoped
|
|
151
|
+
* to a concept. Scores are cosine × {@link kindAwareDecayFactor}; corrections
|
|
152
|
+
* never decay and are pinned in the working set under scan caps.
|
|
153
|
+
*/
|
|
154
|
+
async search(subjectId, queryVector, opts = {}) {
|
|
155
|
+
if (this.dimension !== null && queryVector.length !== this.dimension) {
|
|
156
|
+
throw new Error(`query vector dimension ${queryVector.length} does not match store dimension ${this.dimension}; ` +
|
|
157
|
+
"re-embed before switching embedding models");
|
|
158
|
+
}
|
|
159
|
+
const limit = opts.limit ?? 8;
|
|
160
|
+
const cap = this.options.maxResidentVectors ?? 50_000;
|
|
161
|
+
// Fetch the full subject/topic slice, then pin corrections before cap —
|
|
162
|
+
// SQL LIMIT alone could drop corrections underneath episodic noise.
|
|
163
|
+
const fetched = await this.driver.query(opts.conceptId
|
|
164
|
+
? `SELECT * FROM memory_records WHERE subject_id = ? AND concept_id = ?`
|
|
165
|
+
: `SELECT * FROM memory_records WHERE subject_id = ?`, opts.conceptId ? [subjectId, opts.conceptId] : [subjectId]);
|
|
166
|
+
const rows = pinCorrectionsInWorkingSet(fetched, cap);
|
|
167
|
+
const halfLifeMs = (this.options.episodicHalfLifeDays ?? EPISODIC_HALF_LIFE_DAYS) *
|
|
168
|
+
86_400_000;
|
|
169
|
+
const nowMs = this.options.nowMs?.() ?? Date.now();
|
|
170
|
+
const scored = rows.map((row) => {
|
|
171
|
+
const vector = new Float32Array(row.vector.buffer, row.vector.byteOffset, row.vector.byteLength / 4);
|
|
172
|
+
const similarity = cosine(queryVector, vector);
|
|
173
|
+
const decay = kindAwareDecayFactor(row.kind, row.created_at, nowMs, halfLifeMs);
|
|
174
|
+
return {
|
|
175
|
+
record: {
|
|
176
|
+
id: row.id,
|
|
177
|
+
subjectId: row.subject_id,
|
|
178
|
+
conceptId: row.concept_id,
|
|
179
|
+
text: row.text,
|
|
180
|
+
vector,
|
|
181
|
+
kind: row.kind,
|
|
182
|
+
createdAt: row.created_at,
|
|
183
|
+
},
|
|
184
|
+
score: similarity * decay,
|
|
185
|
+
};
|
|
186
|
+
});
|
|
187
|
+
return scored.sort(compareScoredMemories).slice(0, limit);
|
|
188
|
+
}
|
|
189
|
+
/** Delete one row by id (MemoryInterface.forget). */
|
|
190
|
+
async deleteById(id) {
|
|
191
|
+
await this.driver.execute(`DELETE FROM memory_records WHERE id = ?`, [id]);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Remove episodic records past retention. When `subjectId` is set, only that
|
|
195
|
+
* subject's rows are pruned (subject isolation).
|
|
196
|
+
*/
|
|
197
|
+
async pruneEpisodicOlderThan(cutoff, subjectId) {
|
|
198
|
+
if (subjectId !== undefined && subjectId.trim().length > 0) {
|
|
199
|
+
const sid = subjectId.trim();
|
|
200
|
+
const before = await this.driver.query(`SELECT COUNT(*) AS n FROM memory_records WHERE kind = 'episodic' AND created_at < ? AND subject_id = ?`, [cutoff, sid]);
|
|
201
|
+
await this.driver.execute(`DELETE FROM memory_records WHERE kind = 'episodic' AND created_at < ? AND subject_id = ?`, [cutoff, sid]);
|
|
202
|
+
return before[0]?.n ?? 0;
|
|
203
|
+
}
|
|
204
|
+
const before = await this.driver.query(`SELECT COUNT(*) AS n FROM memory_records WHERE kind = 'episodic' AND created_at < ?`, [cutoff]);
|
|
205
|
+
await this.driver.execute(`DELETE FROM memory_records WHERE kind = 'episodic' AND created_at < ?`, [cutoff]);
|
|
206
|
+
return before[0]?.n ?? 0;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/** Cosine similarity; returns 0 for degenerate zero vectors. */
|
|
210
|
+
function cosine(a, b) {
|
|
211
|
+
let dot = 0;
|
|
212
|
+
let na = 0;
|
|
213
|
+
let nb = 0;
|
|
214
|
+
const len = Math.min(a.length, b.length);
|
|
215
|
+
for (let i = 0; i < len; i++) {
|
|
216
|
+
const x = a[i];
|
|
217
|
+
const y = b[i];
|
|
218
|
+
dot += x * y;
|
|
219
|
+
na += x * x;
|
|
220
|
+
nb += y * y;
|
|
221
|
+
}
|
|
222
|
+
const denom = Math.sqrt(na) * Math.sqrt(nb);
|
|
223
|
+
return denom === 0 ? 0 : dot / denom;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Durable in-memory StorageDriver for the SQL LocalVectorDb issues.
|
|
227
|
+
* execute flushes rows before resolve (CK-02.1 substrate).
|
|
228
|
+
*/
|
|
229
|
+
export function createLocalVectorMemoryDriver() {
|
|
230
|
+
const rows = new Map();
|
|
231
|
+
return {
|
|
232
|
+
rowCount: () => rows.size,
|
|
233
|
+
async execute(sql, params = []) {
|
|
234
|
+
const s = sql.trim();
|
|
235
|
+
if (s.startsWith("CREATE"))
|
|
236
|
+
return;
|
|
237
|
+
if (sql.includes("INSERT OR REPLACE INTO memory_records")) {
|
|
238
|
+
const id = String(params[0]);
|
|
239
|
+
const vector = params[4];
|
|
240
|
+
const blob = vector instanceof Uint8Array
|
|
241
|
+
? vector
|
|
242
|
+
: vector instanceof Float32Array
|
|
243
|
+
? new Uint8Array(vector.buffer.slice(0))
|
|
244
|
+
: new Uint8Array(vector);
|
|
245
|
+
rows.set(id, {
|
|
246
|
+
id,
|
|
247
|
+
subject_id: String(params[1]),
|
|
248
|
+
concept_id: String(params[2]),
|
|
249
|
+
text: String(params[3]),
|
|
250
|
+
vector: blob,
|
|
251
|
+
kind: String(params[5]),
|
|
252
|
+
created_at: String(params[6]),
|
|
253
|
+
});
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (sql.includes("DELETE FROM memory_records WHERE id = ?")) {
|
|
257
|
+
rows.delete(String(params[0]));
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (sql.includes("DELETE FROM memory_records WHERE kind = 'episodic'")) {
|
|
261
|
+
const cutoff = String(params[0]);
|
|
262
|
+
const subjectScoped = sql.includes("subject_id = ?");
|
|
263
|
+
const sid = subjectScoped ? String(params[1]) : null;
|
|
264
|
+
for (const [id, r] of [...rows.entries()]) {
|
|
265
|
+
if (r.kind === "episodic" &&
|
|
266
|
+
r.created_at < cutoff &&
|
|
267
|
+
(sid === null || r.subject_id === sid)) {
|
|
268
|
+
rows.delete(id);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
async query(sql, params = []) {
|
|
274
|
+
if (sql.includes("COUNT(*)")) {
|
|
275
|
+
const cutoff = String(params[0]);
|
|
276
|
+
const subjectScoped = sql.includes("subject_id = ?");
|
|
277
|
+
const sid = subjectScoped ? String(params[1]) : null;
|
|
278
|
+
const n = [...rows.values()].filter((r) => r.kind === "episodic" &&
|
|
279
|
+
r.created_at < cutoff &&
|
|
280
|
+
(sid === null || r.subject_id === sid)).length;
|
|
281
|
+
return [{ n }];
|
|
282
|
+
}
|
|
283
|
+
const subjectId = String(params[0]);
|
|
284
|
+
let bySubject = [...rows.values()].filter((r) => r.subject_id === subjectId);
|
|
285
|
+
if (sql.includes("concept_id = ?")) {
|
|
286
|
+
const conceptId = String(params[1]);
|
|
287
|
+
bySubject = bySubject.filter((r) => r.concept_id === conceptId);
|
|
288
|
+
}
|
|
289
|
+
const limitIdx = sql.includes("concept_id = ?") ? 2 : 1;
|
|
290
|
+
const limit = typeof params[limitIdx] === "number"
|
|
291
|
+
? params[limitIdx]
|
|
292
|
+
: bySubject.length;
|
|
293
|
+
return bySubject.slice(0, limit);
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
//# sourceMappingURL=local_vector_db.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local_vector_db.js","sourceRoot":"","sources":["../src/local_vector_db.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module slm_runtime
|
|
3
|
+
*
|
|
4
|
+
* Pluggable local inference boundary of the Edge Harness.
|
|
5
|
+
*
|
|
6
|
+
* The harness never links against a specific inference engine. Instead it
|
|
7
|
+
* speaks `SlmRuntime`, and adapters bind that interface to whatever the
|
|
8
|
+
* host device offers:
|
|
9
|
+
* - llama.cpp / GGUF (`@moolam/bindings-slm` LlamaCppSlmRuntime;
|
|
10
|
+
* ModelInterface via createLlamaCppModelAdapter / createSlmModelAdapter)
|
|
11
|
+
* - ONNX Runtime Mobile (quantized INT4/INT8 exports)
|
|
12
|
+
* - MediaPipe LLM Inference API (Android AICore)
|
|
13
|
+
* - Apple Foundation Models / MLX (iOS)
|
|
14
|
+
* - any localhost OpenAI-compatible server (Ollama, llamafile)
|
|
15
|
+
*
|
|
16
|
+
* CognitiveCore consumes ModelInterface. Pass a loaded LlamaCppSlmRuntime
|
|
17
|
+
* into edge cognitive bindings, or call createLlamaCppModelAdapter and
|
|
18
|
+
* inject the resulting model into the binding set.
|
|
19
|
+
*
|
|
20
|
+
* This is what makes the Edge Harness sovereign: a district deployment can
|
|
21
|
+
* swap models and engines without touching a line of domain code.
|
|
22
|
+
*/
|
|
23
|
+
/** Declarative description of the local model an adapter has loaded. */
|
|
24
|
+
export interface SlmModelCard {
|
|
25
|
+
/** e.g. "phi-3-mini-4k-instruct-q4_K_M" */
|
|
26
|
+
modelId: string;
|
|
27
|
+
/** Context window in tokens the adapter will actually honor. */
|
|
28
|
+
contextWindow: number;
|
|
29
|
+
/** Quantization descriptor, e.g. "Q4_K_M", "int4-awq", "fp16". */
|
|
30
|
+
quantization: string;
|
|
31
|
+
/** Rough on-device footprint in MiB, used by the harness's load planner. */
|
|
32
|
+
memoryFootprintMiB: number;
|
|
33
|
+
/** Languages the model operates competently in (BCP-47). */
|
|
34
|
+
languages: string[];
|
|
35
|
+
}
|
|
36
|
+
export interface SlmGenerateParams {
|
|
37
|
+
/** Fully-assembled prompt (the harness owns prompt construction). */
|
|
38
|
+
prompt: string;
|
|
39
|
+
maxTokens: number;
|
|
40
|
+
temperature: number;
|
|
41
|
+
/** Hard wall-clock budget; adapters MUST abort and flush on breach. */
|
|
42
|
+
deadlineMs: number;
|
|
43
|
+
stopSequences?: string[];
|
|
44
|
+
}
|
|
45
|
+
export interface SlmGenerateResult {
|
|
46
|
+
text: string;
|
|
47
|
+
/** Tokens emitted per second — fed into CAST as a device-health signal. */
|
|
48
|
+
tokensPerSecond: number;
|
|
49
|
+
finishReason: "stop" | "length" | "deadline" | "aborted";
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The single interface every local inference adapter implements.
|
|
53
|
+
* Adapters must be side-effect free until `load()` is invoked so the
|
|
54
|
+
* harness can enumerate candidates cheaply on constrained devices.
|
|
55
|
+
*
|
|
56
|
+
* Streaming MUST yield CK-03.2 deltas (new text only), never cumulative
|
|
57
|
+
* restatements of prior frames. Implementations SHOULD race an
|
|
58
|
+
* AbortController-equivalent against `deadlineMs` at the native/FFI layer
|
|
59
|
+
* so generation cannot hang the harness thread.
|
|
60
|
+
*/
|
|
61
|
+
export interface SlmRuntime {
|
|
62
|
+
readonly card: SlmModelCard;
|
|
63
|
+
/** Materialize weights into memory. Idempotent. */
|
|
64
|
+
load(): Promise<void>;
|
|
65
|
+
/** Release weights. The harness calls this under memory pressure. */
|
|
66
|
+
unload(): Promise<void>;
|
|
67
|
+
/** Single-shot generation within a strict deadline. */
|
|
68
|
+
generate(params: SlmGenerateParams): Promise<SlmGenerateResult>;
|
|
69
|
+
/** Streaming variant for word-by-word reply rendering (delta frames). */
|
|
70
|
+
generateStream(params: SlmGenerateParams): AsyncIterable<string>;
|
|
71
|
+
/** Embed text for the local vector store. Dimension must be stable. */
|
|
72
|
+
embed(text: string): Promise<Float32Array>;
|
|
73
|
+
}
|
|
74
|
+
/** Init/load obligation named on typed failures (never silent catch-and-continue). */
|
|
75
|
+
export declare const EDGE_SLM_LOAD_OBLIGATION = "EDGE.SLM_LOAD";
|
|
76
|
+
/** Closed set of SlmRuntime load failure classes. */
|
|
77
|
+
export type SlmRuntimeInitFailureClass = "missing_weights" | "corrupt_weights" | "config";
|
|
78
|
+
/**
|
|
79
|
+
* Typed initialization/load failure for local model weights.
|
|
80
|
+
* Hosts MUST surface this to the operator — never retry unboundedly.
|
|
81
|
+
*/
|
|
82
|
+
export declare class SlmRuntimeInitError extends Error {
|
|
83
|
+
readonly name = "SlmRuntimeInitError";
|
|
84
|
+
readonly failureClass: SlmRuntimeInitFailureClass;
|
|
85
|
+
readonly obligationId: string;
|
|
86
|
+
readonly reason: "missing" | "corrupt" | "config";
|
|
87
|
+
constructor(message: string, opts: {
|
|
88
|
+
failureClass: SlmRuntimeInitFailureClass;
|
|
89
|
+
reason: "missing" | "corrupt" | "config";
|
|
90
|
+
obligationId?: string;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/** Metadata-only telemetry for SLM load/unload (never utterance/prompt bodies). */
|
|
94
|
+
export type SlmRuntimeTelemetryEvent = {
|
|
95
|
+
event: "edge_agent.slm_runtime";
|
|
96
|
+
op: "load" | "unload";
|
|
97
|
+
outcome: "ok" | "init_error";
|
|
98
|
+
modelId: string;
|
|
99
|
+
subjectId?: string;
|
|
100
|
+
deviceId?: string;
|
|
101
|
+
failureClass?: SlmRuntimeInitFailureClass;
|
|
102
|
+
obligationId?: string;
|
|
103
|
+
reason?: "missing" | "corrupt" | "config";
|
|
104
|
+
};
|
|
105
|
+
/** Magic prefix for drill / on-disk weight fixtures (not a full GGUF parser). */
|
|
106
|
+
export declare const SLM_WEIGHTS_MAGIC = "SUTRA-WEIGHTS-v1";
|
|
107
|
+
export type LocalWeightSlmRuntimeOptions = {
|
|
108
|
+
/** Absolute or relative path to on-disk model weights. */
|
|
109
|
+
weightsPath: string;
|
|
110
|
+
subjectId?: string;
|
|
111
|
+
deviceId?: string;
|
|
112
|
+
onTelemetry?: (event: SlmRuntimeTelemetryEvent) => void;
|
|
113
|
+
/**
|
|
114
|
+
* Optional custom integrity check after magic-header validation.
|
|
115
|
+
* Return false → corrupt_weights.
|
|
116
|
+
*/
|
|
117
|
+
inspectWeights?: (bytes: Uint8Array) => boolean;
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* File-backed SlmRuntime load seam used for edge degradation drills and
|
|
121
|
+
* host deployments that materialize weights from disk before inference.
|
|
122
|
+
*
|
|
123
|
+
* Missing or corrupt weights → {@link SlmRuntimeInitError} once per `load()`
|
|
124
|
+
* (no internal retry loop). Generation requires a prior successful load.
|
|
125
|
+
*/
|
|
126
|
+
export declare class LocalWeightSlmRuntime implements SlmRuntime {
|
|
127
|
+
readonly card: SlmModelCard;
|
|
128
|
+
private readonly options;
|
|
129
|
+
private loaded;
|
|
130
|
+
private loadAttempts;
|
|
131
|
+
constructor(card: SlmModelCard, options: LocalWeightSlmRuntimeOptions);
|
|
132
|
+
/** How many times `load()` has been invoked (crash-loop detection). */
|
|
133
|
+
get loadAttemptCount(): number;
|
|
134
|
+
get isLoaded(): boolean;
|
|
135
|
+
load(): Promise<void>;
|
|
136
|
+
unload(): Promise<void>;
|
|
137
|
+
generate(_params: SlmGenerateParams): Promise<SlmGenerateResult>;
|
|
138
|
+
generateStream(params: SlmGenerateParams): AsyncIterable<string>;
|
|
139
|
+
embed(_text: string): Promise<Float32Array>;
|
|
140
|
+
private failLoad;
|
|
141
|
+
private emit;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Adapter for any localhost OpenAI-compatible endpoint (Ollama, llamafile,
|
|
145
|
+
* llama.cpp server). This is the reference adapter used by the Playground and
|
|
146
|
+
* by desktop deployments; mobile targets ship their own native adapters.
|
|
147
|
+
*/
|
|
148
|
+
export declare class OpenAiCompatibleSlmRuntime implements SlmRuntime {
|
|
149
|
+
readonly card: SlmModelCard;
|
|
150
|
+
private readonly baseUrl;
|
|
151
|
+
constructor(card: SlmModelCard, baseUrl?: string);
|
|
152
|
+
load(): Promise<void>;
|
|
153
|
+
unload(): Promise<void>;
|
|
154
|
+
generate(params: SlmGenerateParams): Promise<SlmGenerateResult>;
|
|
155
|
+
generateStream(params: SlmGenerateParams): AsyncIterable<string>;
|
|
156
|
+
embed(text: string): Promise<Float32Array>;
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=slm_runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slm_runtime.d.ts","sourceRoot":"","sources":["../src/slm_runtime.ts"],"names":[],"mappings":"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"}
|