@rudderjs/ai 1.18.1 → 1.18.3

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/dist/doctor.js CHANGED
@@ -1,5 +1,68 @@
1
- // @rudderjs/ai is deprecated. The AI engine now lives in @gemstack/ai-sdk.
2
- // This module re-exports it for backwards compatibility; import from
3
- // '@gemstack/ai-sdk/doctor' directly in new code.
4
- export * from '@gemstack/ai-sdk/doctor';
1
+ // AI doctor checks contributed by @rudderjs/ai. This is a Rudder binding: it
2
+ // registers into @rudderjs/console's doctor registry, so it lives here rather
3
+ // than in the agnostic @gemstack/ai-sdk engine.
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import { registerDoctorCheck } from '@rudderjs/console';
7
+ function readFileSafe(rel) {
8
+ try {
9
+ return fs.readFileSync(path.join(process.cwd(), rel), 'utf-8');
10
+ }
11
+ catch {
12
+ return null;
13
+ }
14
+ }
15
+ // Maps provider driver name → env var the user must set. Mirrors the
16
+ // driver names listed in @gemstack/ai-sdk's provider implementations.
17
+ const PROVIDER_ENV = {
18
+ anthropic: 'ANTHROPIC_API_KEY',
19
+ openai: 'OPENAI_API_KEY',
20
+ google: 'GOOGLE_AI_API_KEY',
21
+ bedrock: 'AWS_ACCESS_KEY_ID',
22
+ groq: 'GROQ_API_KEY',
23
+ openrouter: 'OPENROUTER_API_KEY',
24
+ // ollama, lmstudio: local — no key needed.
25
+ };
26
+ // Extracts driver names referenced by config/ai.ts WITHOUT importing the
27
+ // module. We grep for `driver: '<name>'` literals — covers the scaffolded
28
+ // shape.
29
+ function declaredProviders() {
30
+ const text = readFileSafe('config/ai.ts') ??
31
+ readFileSafe('config/ai.js') ??
32
+ readFileSafe('config/ai.mjs') ?? '';
33
+ const matches = [...text.matchAll(/driver\s*:\s*['"]([^'"]+)['"]/g)];
34
+ return [...new Set(matches.map(m => m[1]).filter(Boolean))];
35
+ }
36
+ registerDoctorCheck({
37
+ id: 'ai:provider-keys',
38
+ category: 'ai',
39
+ title: 'AI provider API keys',
40
+ run() {
41
+ const providers = declaredProviders();
42
+ if (providers.length === 0) {
43
+ return { status: 'ok', message: 'no config/ai.ts or no providers declared — skip' };
44
+ }
45
+ const needsKey = providers.filter(p => p in PROVIDER_ENV);
46
+ if (needsKey.length === 0) {
47
+ return { status: 'ok', message: `${providers.length} provider(s) — all local (no keys required)` };
48
+ }
49
+ const missing = needsKey.filter(p => !process.env[PROVIDER_ENV[p]]);
50
+ if (missing.length === needsKey.length) {
51
+ return {
52
+ status: 'warn',
53
+ message: `none of ${needsKey.length} cloud provider(s) have an API key set`,
54
+ fix: `Set at least one of: ${needsKey.map(p => PROVIDER_ENV[p]).join(', ')} (or remove the providers from config/ai.ts if unused)`,
55
+ detail: `Declared providers: ${needsKey.join(', ')}`,
56
+ };
57
+ }
58
+ if (missing.length > 0) {
59
+ return {
60
+ status: 'warn',
61
+ message: `${needsKey.length - missing.length}/${needsKey.length} cloud provider(s) have keys`,
62
+ fix: `Set missing keys: ${missing.map(p => PROVIDER_ENV[p]).join(', ')} (or remove the providers from config/ai.ts if unused)`,
63
+ };
64
+ }
65
+ return { status: 'ok', message: `${needsKey.length} cloud provider(s), all keys present` };
66
+ },
67
+ });
5
68
  //# sourceMappingURL=doctor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,qEAAqE;AACrE,kDAAkD;AAClD,cAAc,yBAAyB,CAAA"}
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- export * from '@gemstack/ai-sdk/mcp';
1
+ export * from '@gemstack/ai-mcp';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAGA,cAAc,sBAAsB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAIA,cAAc,kBAAkB,CAAA"}
package/dist/mcp/index.js CHANGED
@@ -1,5 +1,6 @@
1
- // @rudderjs/ai is deprecated. The AI engine now lives in @gemstack/ai-sdk.
2
- // This module re-exports it for backwards compatibility; import from
3
- // '@gemstack/ai-sdk/mcp' directly in new code.
4
- export * from '@gemstack/ai-sdk/mcp';
1
+ // @rudderjs/ai is deprecated. The agent<->MCP bridge now lives in
2
+ // @gemstack/ai-mcp (it graduated out of @gemstack/ai-sdk at 0.3.0). This
3
+ // module re-exports it for backwards compatibility; import from
4
+ // '@gemstack/ai-mcp' directly in new code.
5
+ export * from '@gemstack/ai-mcp';
5
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,qEAAqE;AACrE,+CAA+C;AAC/C,cAAc,sBAAsB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,yEAAyE;AACzE,gEAAgE;AAChE,2CAA2C;AAC3C,cAAc,kBAAkB,CAAA"}
@@ -1,2 +1,121 @@
1
- export * from '@gemstack/ai-sdk/memory-embedding';
1
+ /**
2
+ * `@rudderjs/ai/memory-embedding` — embedding-backed {@link UserMemory}
3
+ * for #A4 Phase 5.
4
+ *
5
+ * Composes Phase 4's {@link OrmUserMemory} with the embedding
6
+ * provider registered on {@link AiRegistry}: `remember()` embeds the
7
+ * fact and writes the Float32-packed vector into the row's
8
+ * `embedding` column; `recall()` embeds the query and ranks by
9
+ * cosine similarity. `forget()` / `forgetAll()` delegate to the
10
+ * inner store — the embedding lives in the same row, so deleting
11
+ * the row deletes the vector. GDPR right-to-be-forgotten cascades
12
+ * automatically.
13
+ *
14
+ * v1 is **pure-JS cosine over the user's full set** — fine up to
15
+ * a few thousand facts per user. For larger workloads, B7 lands a
16
+ * pgvector-backed `EmbeddingUserMemory` that pushes the dot-product
17
+ * into the database.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import { OrmUserMemory } from '@rudderjs/ai/memory-orm'
22
+ * import { EmbeddingUserMemory } from '@rudderjs/ai/memory-embedding'
23
+ *
24
+ * const memory = new EmbeddingUserMemory({
25
+ * inner: new OrmUserMemory(),
26
+ * model: 'openai/text-embedding-3-small',
27
+ * threshold: 0.5, // cosine floor; matches below are dropped
28
+ * })
29
+ * ```
30
+ *
31
+ * **Pre-Phase-5 facts** (rows with `embedding === null`) fall back to
32
+ * token-overlap matching against the `fact` column — same shape as
33
+ * `MemoryUserMemory.recall()`. So upgrading from `OrmUserMemory` to
34
+ * `EmbeddingUserMemory` doesn't lose recall on existing rows; new
35
+ * `remember()` calls populate the embedding column going forward.
36
+ */
37
+ import { OrmUserMemory } from '../memory-orm/index.js';
38
+ import type { MemoryEntry, UserMemory } from '@gemstack/ai-sdk';
39
+ export interface EmbeddingUserMemoryOptions {
40
+ /**
41
+ * The composed inner store. Must be {@link OrmUserMemory} for v1
42
+ * — the composer reads/writes the `embedding Bytes?` column on
43
+ * the same row. Other backends (Pinecone, Weaviate) implement
44
+ * their own.
45
+ */
46
+ inner: OrmUserMemory;
47
+ /**
48
+ * Embedding model id (`'<provider>/<model>'`). Used for both
49
+ * fact embedding on `remember()` and query embedding on
50
+ * `recall()`. Default: whatever `AI.embed()` picks (`AiRegistry`
51
+ * default).
52
+ */
53
+ model?: string;
54
+ /**
55
+ * Cosine-similarity floor in `[-1, 1]`. Matches below the
56
+ * threshold are dropped before sorting. Default `0` — return
57
+ * everything ranked. Tighten for higher precision; loosen for
58
+ * higher recall.
59
+ */
60
+ threshold?: number;
61
+ /**
62
+ * Optional fallback for rows whose `embedding` column is `null`
63
+ * (rows persisted without the embedding composer wired in).
64
+ *
65
+ * - `'token-overlap'` (default) — score 0 if any ≥3-char token
66
+ * from the query appears in the row's `fact`. Lets you
67
+ * upgrade `OrmUserMemory` → `EmbeddingUserMemory` without
68
+ * losing recall on existing rows.
69
+ * - `'skip'` — drop null-embedding rows entirely.
70
+ */
71
+ nullEmbeddingFallback?: 'token-overlap' | 'skip';
72
+ }
73
+ export declare class EmbeddingUserMemory implements UserMemory {
74
+ private readonly inner;
75
+ private readonly model;
76
+ private readonly threshold;
77
+ private readonly fallback;
78
+ constructor(opts: EmbeddingUserMemoryOptions);
79
+ remember(userId: string, fact: string, opts?: {
80
+ tags?: string[];
81
+ score?: number;
82
+ }): Promise<MemoryEntry>;
83
+ recall(userId: string, query: string, opts?: {
84
+ limit?: number;
85
+ tags?: string[];
86
+ }): Promise<MemoryEntry[]>;
87
+ forget(userId: string, factId: string): Promise<void>;
88
+ list(userId: string, opts?: {
89
+ tags?: string[];
90
+ limit?: number;
91
+ }): Promise<MemoryEntry[]>;
92
+ forgetAll(userId: string): Promise<void>;
93
+ /**
94
+ * Single-string embedding via the {@link AI} facade. Returns the
95
+ * first (and only) embedding vector. Throws on provider/network
96
+ * failure; callers route through try/catch and degrade.
97
+ */
98
+ private embed;
99
+ }
100
+ /**
101
+ * Pack a `number[]` into a Float32 byte buffer. 4 bytes per dim;
102
+ * a 1536-dim OpenAI embedding compresses to 6144 bytes.
103
+ *
104
+ * Uses `ArrayBuffer` + `Float32Array` so the output is a portable
105
+ * `Uint8Array` (works in Node, browser, RN). Prisma's `Bytes`
106
+ * column accepts both `Uint8Array` and `Buffer`.
107
+ */
108
+ export declare function serializeVector(v: number[]): Uint8Array;
109
+ /**
110
+ * Reverse of {@link serializeVector}. Reads the underlying byte
111
+ * buffer as Float32 and returns a fresh `number[]` so callers can
112
+ * mutate without affecting the source row.
113
+ */
114
+ export declare function deserializeVector(bytes: Uint8Array): number[];
115
+ /**
116
+ * Cosine similarity in `[-1, 1]`. Returns `0` when either vector
117
+ * has zero magnitude, or when lengths don't match (defensive — should
118
+ * never happen if remember/recall use the same embedding model).
119
+ */
120
+ export declare function cosineSimilarity(a: number[], b: number[]): number;
2
121
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/memory-embedding/index.ts"],"names":[],"mappings":"AAGA,cAAc,mCAAmC,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/memory-embedding/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAGH,OAAO,EAAE,aAAa,EAAoB,MAAM,wBAAwB,CAAA;AACxE,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACX,MAAM,kBAAkB,CAAA;AAEzB,MAAM,WAAW,0BAA0B;IACzC;;;;;OAKG;IACH,KAAK,EAAE,aAAa,CAAA;IACpB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;;;;;OASG;IACH,qBAAqB,CAAC,EAAE,eAAe,GAAG,MAAM,CAAA;CACjD;AAED,qBAAa,mBAAoB,YAAW,UAAU;IACpD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuB;IAC7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA0B;gBAEvC,IAAI,EAAE,0BAA0B;IAOtC,QAAQ,CACZ,MAAM,EAAE,MAAM,EACd,IAAI,EAAI,MAAM,EACd,IAAI,CAAC,EAAG;QAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAC1C,OAAO,CAAC,WAAW,CAAC;IAkBjB,MAAM,CACV,MAAM,EAAE,MAAM,EACd,KAAK,EAAG,MAAM,EACd,IAAI,CAAC,EAAG;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,GAC1C,OAAO,CAAC,WAAW,EAAE,CAAC;IA4CnB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMrD,IAAI,CACR,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAG;QAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAC1C,OAAO,CAAC,WAAW,EAAE,CAAC;IAInB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK9C;;;;OAIG;YACW,KAAK;CAMpB;AAID;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,UAAU,CAKvD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,EAAE,CAK7D;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAcjE"}
@@ -1,5 +1,229 @@
1
- // @rudderjs/ai is deprecated. The AI engine now lives in @gemstack/ai-sdk.
2
- // This module re-exports it for backwards compatibility; import from
3
- // '@gemstack/ai-sdk/memory-embedding' directly in new code.
4
- export * from '@gemstack/ai-sdk/memory-embedding';
1
+ /**
2
+ * `@rudderjs/ai/memory-embedding` embedding-backed {@link UserMemory}
3
+ * for #A4 Phase 5.
4
+ *
5
+ * Composes Phase 4's {@link OrmUserMemory} with the embedding
6
+ * provider registered on {@link AiRegistry}: `remember()` embeds the
7
+ * fact and writes the Float32-packed vector into the row's
8
+ * `embedding` column; `recall()` embeds the query and ranks by
9
+ * cosine similarity. `forget()` / `forgetAll()` delegate to the
10
+ * inner store — the embedding lives in the same row, so deleting
11
+ * the row deletes the vector. GDPR right-to-be-forgotten cascades
12
+ * automatically.
13
+ *
14
+ * v1 is **pure-JS cosine over the user's full set** — fine up to
15
+ * a few thousand facts per user. For larger workloads, B7 lands a
16
+ * pgvector-backed `EmbeddingUserMemory` that pushes the dot-product
17
+ * into the database.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import { OrmUserMemory } from '@rudderjs/ai/memory-orm'
22
+ * import { EmbeddingUserMemory } from '@rudderjs/ai/memory-embedding'
23
+ *
24
+ * const memory = new EmbeddingUserMemory({
25
+ * inner: new OrmUserMemory(),
26
+ * model: 'openai/text-embedding-3-small',
27
+ * threshold: 0.5, // cosine floor; matches below are dropped
28
+ * })
29
+ * ```
30
+ *
31
+ * **Pre-Phase-5 facts** (rows with `embedding === null`) fall back to
32
+ * token-overlap matching against the `fact` column — same shape as
33
+ * `MemoryUserMemory.recall()`. So upgrading from `OrmUserMemory` to
34
+ * `EmbeddingUserMemory` doesn't lose recall on existing rows; new
35
+ * `remember()` calls populate the embedding column going forward.
36
+ */
37
+ import { AI } from '@gemstack/ai-sdk';
38
+ import { UserMemoryRecord } from '../memory-orm/index.js';
39
+ export class EmbeddingUserMemory {
40
+ inner;
41
+ model;
42
+ threshold;
43
+ fallback;
44
+ constructor(opts) {
45
+ this.inner = opts.inner;
46
+ if (opts.model !== undefined)
47
+ this.model = opts.model;
48
+ this.threshold = opts.threshold ?? 0;
49
+ this.fallback = opts.nullEmbeddingFallback ?? 'token-overlap';
50
+ }
51
+ async remember(userId, fact, opts) {
52
+ const entry = await this.inner.remember(userId, fact, opts);
53
+ // Best-effort embed + persist. Failures are logged via the inner
54
+ // store still having the entry; we don't break the caller.
55
+ try {
56
+ const vector = await this.embed(fact);
57
+ await UserMemoryRecord.update(entry.id, {
58
+ embedding: serializeVector(vector),
59
+ });
60
+ }
61
+ catch {
62
+ // Embedding failed (network, missing peer SDK). The row is
63
+ // already in the store; recall will fall back to
64
+ // token-overlap if the column stays null. No-op.
65
+ }
66
+ return entry;
67
+ }
68
+ async recall(userId, query, opts) {
69
+ let queryVector = null;
70
+ try {
71
+ queryVector = await this.embed(query);
72
+ }
73
+ catch {
74
+ // Embed failed — fall through to token-overlap on every row.
75
+ }
76
+ const rows = await UserMemoryRecord.where('userId', userId).get();
77
+ const wanted = opts?.tags;
78
+ const queryTokens = tokenize(query);
79
+ const scored = [];
80
+ for (const row of rows) {
81
+ const entry = rowToEntry(row);
82
+ if (!matchesTags(entry, wanted))
83
+ continue;
84
+ let score;
85
+ if (queryVector !== null && row.embedding !== null && row.embedding !== undefined) {
86
+ const factVector = deserializeVector(row.embedding);
87
+ score = cosineSimilarity(queryVector, factVector);
88
+ }
89
+ else if (this.fallback === 'skip') {
90
+ continue;
91
+ }
92
+ else {
93
+ // token-overlap fallback — score 0 (mid-range) if any
94
+ // token matches, otherwise drop.
95
+ if (factHasAnyToken(entry.fact, queryTokens)) {
96
+ score = 0;
97
+ }
98
+ else {
99
+ continue;
100
+ }
101
+ }
102
+ if (score >= this.threshold) {
103
+ scored.push({ entry, score });
104
+ }
105
+ }
106
+ scored.sort((a, b) => b.score - a.score);
107
+ const capped = capLimit(scored, opts?.limit);
108
+ return capped.map(s => ({ ...s.entry, score: s.score }));
109
+ }
110
+ async forget(userId, factId) {
111
+ // The embedding lives in the same row — deleting via the inner
112
+ // store deletes the vector too. GDPR cascade is automatic.
113
+ return this.inner.forget(userId, factId);
114
+ }
115
+ async list(userId, opts) {
116
+ return this.inner.list(userId, opts);
117
+ }
118
+ async forgetAll(userId) {
119
+ if (!this.inner.forgetAll)
120
+ return;
121
+ return this.inner.forgetAll(userId);
122
+ }
123
+ /**
124
+ * Single-string embedding via the {@link AI} facade. Returns the
125
+ * first (and only) embedding vector. Throws on provider/network
126
+ * failure; callers route through try/catch and degrade.
127
+ */
128
+ async embed(text) {
129
+ const result = await AI.embed(text, this.model ? { model: this.model } : undefined);
130
+ const vec = result.embeddings[0];
131
+ if (!vec)
132
+ throw new Error('[ai-sdk] embed() returned no vectors');
133
+ return vec;
134
+ }
135
+ }
136
+ // ─── Vector + similarity helpers (exported for tests + B7) ─────
137
+ /**
138
+ * Pack a `number[]` into a Float32 byte buffer. 4 bytes per dim;
139
+ * a 1536-dim OpenAI embedding compresses to 6144 bytes.
140
+ *
141
+ * Uses `ArrayBuffer` + `Float32Array` so the output is a portable
142
+ * `Uint8Array` (works in Node, browser, RN). Prisma's `Bytes`
143
+ * column accepts both `Uint8Array` and `Buffer`.
144
+ */
145
+ export function serializeVector(v) {
146
+ const buf = new ArrayBuffer(v.length * 4);
147
+ const view = new Float32Array(buf);
148
+ for (let i = 0; i < v.length; i++)
149
+ view[i] = v[i];
150
+ return new Uint8Array(buf);
151
+ }
152
+ /**
153
+ * Reverse of {@link serializeVector}. Reads the underlying byte
154
+ * buffer as Float32 and returns a fresh `number[]` so callers can
155
+ * mutate without affecting the source row.
156
+ */
157
+ export function deserializeVector(bytes) {
158
+ // The `bytes.buffer` may be a slice; honor byteOffset + byteLength
159
+ // so we don't read into adjacent memory.
160
+ const view = new Float32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 4);
161
+ return Array.from(view);
162
+ }
163
+ /**
164
+ * Cosine similarity in `[-1, 1]`. Returns `0` when either vector
165
+ * has zero magnitude, or when lengths don't match (defensive — should
166
+ * never happen if remember/recall use the same embedding model).
167
+ */
168
+ export function cosineSimilarity(a, b) {
169
+ if (a.length !== b.length)
170
+ return 0;
171
+ let dot = 0;
172
+ let magA = 0;
173
+ let magB = 0;
174
+ for (let i = 0; i < a.length; i++) {
175
+ const ai = a[i];
176
+ const bi = b[i];
177
+ dot += ai * bi;
178
+ magA += ai * ai;
179
+ magB += bi * bi;
180
+ }
181
+ if (magA === 0 || magB === 0)
182
+ return 0;
183
+ return dot / (Math.sqrt(magA) * Math.sqrt(magB));
184
+ }
185
+ // ─── Internal helpers ─────────────────────────────────────
186
+ function rowToEntry(row) {
187
+ const tags = row.getTags();
188
+ const out = {
189
+ id: row.id,
190
+ userId: row.userId,
191
+ fact: row.fact,
192
+ createdAt: row.createdAt,
193
+ };
194
+ if (tags.length > 0)
195
+ out.tags = tags;
196
+ if (row.score != null)
197
+ out.score = row.score;
198
+ if (row.updatedAt != null)
199
+ out.updatedAt = row.updatedAt;
200
+ return out;
201
+ }
202
+ function tokenize(s) {
203
+ const out = new Set();
204
+ for (const tok of s.toLowerCase().split(/[^a-z0-9]+/)) {
205
+ if (tok.length >= 3)
206
+ out.add(tok);
207
+ }
208
+ return out;
209
+ }
210
+ function factHasAnyToken(fact, queryTokens) {
211
+ if (queryTokens.size === 0)
212
+ return true;
213
+ const factTokens = tokenize(fact);
214
+ for (const t of factTokens)
215
+ if (queryTokens.has(t))
216
+ return true;
217
+ return false;
218
+ }
219
+ function matchesTags(entry, wanted) {
220
+ if (!wanted || wanted.length === 0)
221
+ return true;
222
+ if (!entry.tags || entry.tags.length === 0)
223
+ return false;
224
+ return wanted.every(t => entry.tags.includes(t));
225
+ }
226
+ function capLimit(items, limit) {
227
+ return limit !== undefined && limit > 0 ? items.slice(0, limit) : items;
228
+ }
5
229
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/memory-embedding/index.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,qEAAqE;AACrE,4DAA4D;AAC5D,cAAc,mCAAmC,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/memory-embedding/index.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,118 @@
1
- export * from '@gemstack/ai-sdk/memory-orm';
1
+ /**
2
+ * `@rudderjs/ai/memory-orm` — ORM-backed {@link UserMemory} for #A4 Phase 4.
3
+ *
4
+ * Stores per-user facts in a `UserMemory` table via the registered
5
+ * `@rudderjs/orm` adapter (Prisma today; Drizzle as well once the user's
6
+ * tables are wired). Drop-in alongside Phase 1's in-process
7
+ * `MemoryUserMemory`.
8
+ *
9
+ * Wire it from your AI config:
10
+ *
11
+ * ```ts
12
+ * // config/ai.ts
13
+ * import type { AiConfig } from '@gemstack/ai-sdk'
14
+ * import { OrmUserMemory } from '@rudderjs/ai/memory-orm'
15
+ *
16
+ * export default {
17
+ * default: 'anthropic/claude-sonnet-4-5',
18
+ * providers: { ... },
19
+ * memory: new OrmUserMemory(),
20
+ * } satisfies AiConfig
21
+ * ```
22
+ *
23
+ * The schema lives at `@rudderjs/ai/memory-orm`'s {@link userMemoryPrismaSchema}
24
+ * — copy it into your Prisma schema. The optional `embedding Bytes?`
25
+ * column is shipped here in Phase 4 (intentionally nullable) so Phase 5's
26
+ * `EmbeddingUserMemory` can populate it without forcing an additive
27
+ * migration.
28
+ */
29
+ import { Model } from '@rudderjs/orm';
30
+ import type { MemoryEntry, UserMemory } from '@gemstack/ai-sdk';
31
+ /**
32
+ * The Model row backing {@link OrmUserMemory}. Exposed so apps that
33
+ * want their own queries (admin views, audit dumps) can use the
34
+ * familiar `UserMemoryRecord.where(...).get()` instead of routing
35
+ * everything through the {@link UserMemory} interface.
36
+ *
37
+ * Tags persist as a JSON-encoded string in the `tags` column — both
38
+ * Prisma's portable `String?` and Drizzle's `text` work without
39
+ * needing native array columns. The {@link UserMemory.recall} path
40
+ * filters tags in JavaScript for the same reason.
41
+ *
42
+ * The `embedding Bytes?` column is in the schema as of Phase 4
43
+ * (nullable) so `@rudderjs/ai/memory-embedding`'s `EmbeddingUserMemory`
44
+ * (Phase 5) writes the Float32-packed vector here on `remember()` and
45
+ * reads it for cosine recall. `OrmUserMemory` ignores it — the
46
+ * column stays `null` for any row stored without the embedding
47
+ * composer.
48
+ */
49
+ export declare class UserMemoryRecord extends Model {
50
+ static table: string;
51
+ static fillable: string[];
52
+ id: string;
53
+ userId: string;
54
+ fact: string;
55
+ /** JSON-encoded `string[]` or null. Use `getTags()` for the parsed shape. */
56
+ tags: string | null;
57
+ score: number | null;
58
+ /**
59
+ * Float32-packed vector serialized via
60
+ * `@rudderjs/ai/memory-embedding`'s `serializeVector` /
61
+ * `deserializeVector`. `null` when the row was stored without the
62
+ * embedding composer (Phase 4-only setups).
63
+ */
64
+ embedding: Uint8Array | null;
65
+ createdAt: Date;
66
+ updatedAt: Date | null;
67
+ /** Parsed tags array; empty when nothing was stored. */
68
+ getTags(): string[];
69
+ }
70
+ /**
71
+ * `UserMemory` implementation that persists rows to the registered
72
+ * ORM adapter. Designed for production use — the in-process
73
+ * `MemoryUserMemory` is for tests and dev.
74
+ *
75
+ * Adapter coverage:
76
+ * - Prisma — works out of the box; copy {@link userMemoryPrismaSchema}
77
+ * into your schema.
78
+ * - Drizzle — works once you define a table matching the schema's
79
+ * columns and register it via `tables: { userMemory: <table> }` on
80
+ * the `drizzle()` config.
81
+ *
82
+ * Recall semantics: case-insensitive **token-OR-LIKE** matching against
83
+ * the `fact` column. The query is tokenized on non-alphanumeric
84
+ * boundaries (≥3-char tokens) and any row whose `fact` matches at
85
+ * least one token via `LIKE %tok%` is returned. Mirrors Phase 1's
86
+ * `MemoryUserMemory.recall()` behavior so the two backends are
87
+ * swap-compatible. Tag scope is applied JS-side after fetch — pushing
88
+ * tag-array filtering into the WHERE is adapter-specific and lands in a
89
+ * follow-up.
90
+ */
91
+ export declare class OrmUserMemory implements UserMemory {
92
+ remember(userId: string, fact: string, opts?: {
93
+ tags?: string[];
94
+ score?: number;
95
+ }): Promise<MemoryEntry>;
96
+ recall(userId: string, query: string, opts?: {
97
+ limit?: number;
98
+ tags?: string[];
99
+ }): Promise<MemoryEntry[]>;
100
+ forget(userId: string, factId: string): Promise<void>;
101
+ list(userId: string, opts?: {
102
+ tags?: string[];
103
+ limit?: number;
104
+ }): Promise<MemoryEntry[]>;
105
+ forgetAll(userId: string): Promise<void>;
106
+ }
107
+ /**
108
+ * Reference Prisma schema for `OrmUserMemory`. Copy into your
109
+ * `prisma/schema/<file>.prisma` (or paste alongside an existing
110
+ * model). The `embedding Bytes?` column is intentionally nullable so
111
+ * Phase 5's `EmbeddingUserMemory` becomes additive — no schema
112
+ * migration when you upgrade.
113
+ *
114
+ * SQLite stores `Bytes` as `BLOB`; Postgres stores it as `bytea`.
115
+ * Both work for the dot-product implementation Phase 5 will use.
116
+ */
117
+ export declare const userMemoryPrismaSchema = "model UserMemory {\n id String @id @default(cuid())\n userId String\n fact String\n /// JSON-encoded `string[]` of tags, or null\n tags String?\n /// Confidence score in [0, 1] \u2014 extract sets this from the model's self-rating\n score Float?\n /// Phase 5 \u2014 vector embedding for cosine recall (nullable so Phase 4 ignores it)\n embedding Bytes?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([userId])\n}\n";
2
118
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/memory-orm/index.ts"],"names":[],"mappings":"AAGA,cAAc,6BAA6B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/memory-orm/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AACrC,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACX,MAAM,kBAAkB,CAAA;AAIzB;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,OAAgB,KAAK,SAAe;IAEpC,OAAgB,QAAQ,WAAmD;IAEnE,EAAE,EAAS,MAAM,CAAA;IACjB,MAAM,EAAK,MAAM,CAAA;IACjB,IAAI,EAAO,MAAM,CAAA;IACzB,6EAA6E;IACrE,IAAI,EAAO,MAAM,GAAG,IAAI,CAAA;IACxB,KAAK,EAAM,MAAM,GAAG,IAAI,CAAA;IAChC;;;;;OAKG;IACK,SAAS,EAAE,UAAU,GAAG,IAAI,CAAA;IAC5B,SAAS,EAAE,IAAI,CAAA;IACf,SAAS,EAAE,IAAI,GAAG,IAAI,CAAA;IAE9B,wDAAwD;IACxD,OAAO,IAAI,MAAM,EAAE;CASpB;AAID;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,aAAc,YAAW,UAAU;IACxC,QAAQ,CACZ,MAAM,EAAE,MAAM,EACd,IAAI,EAAI,MAAM,EACd,IAAI,CAAC,EAAG;QAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAC1C,OAAO,CAAC,WAAW,CAAC;IASjB,MAAM,CACV,MAAM,EAAE,MAAM,EACd,KAAK,EAAG,MAAM,EACd,IAAI,CAAC,EAAG;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,GAC1C,OAAO,CAAC,WAAW,EAAE,CAAC;IAgBnB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrD,IAAI,CACR,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAG;QAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAC1C,OAAO,CAAC,WAAW,EAAE,CAAC;IAMnB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG/C;AAID;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB,4eAelC,CAAA"}