@rudderjs/ai 1.5.0 → 1.6.1
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/README.md +482 -4
- package/boost/guidelines.md +60 -0
- package/boost/skills/ai-agents/SKILL.md +7 -0
- package/boost/skills/ai-tools/SKILL.md +7 -0
- package/dist/agent.d.ts +35 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +118 -16
- package/dist/agent.js.map +1 -1
- package/dist/budget/pricing.d.ts +124 -0
- package/dist/budget/pricing.d.ts.map +1 -0
- package/dist/budget/pricing.js +175 -0
- package/dist/budget/pricing.js.map +1 -0
- package/dist/budget/storage.d.ts +104 -0
- package/dist/budget/storage.d.ts.map +1 -0
- package/dist/budget/storage.js +0 -0
- package/dist/budget/storage.js.map +1 -0
- package/dist/budget/with-budget.d.ts +119 -0
- package/dist/budget/with-budget.d.ts.map +1 -0
- package/dist/budget/with-budget.js +175 -0
- package/dist/budget/with-budget.js.map +1 -0
- package/dist/budget-orm/index.d.ts +96 -0
- package/dist/budget-orm/index.d.ts.map +1 -0
- package/dist/budget-orm/index.js +177 -0
- package/dist/budget-orm/index.js.map +1 -0
- package/dist/commands/ai-eval.d.ts +93 -0
- package/dist/commands/ai-eval.d.ts.map +1 -0
- package/dist/commands/ai-eval.js +378 -0
- package/dist/commands/ai-eval.js.map +1 -0
- package/dist/computer-use/actions.d.ts +214 -0
- package/dist/computer-use/actions.d.ts.map +1 -0
- package/dist/computer-use/actions.js +48 -0
- package/dist/computer-use/actions.js.map +1 -0
- package/dist/computer-use/errors.d.ts +57 -0
- package/dist/computer-use/errors.d.ts.map +1 -0
- package/dist/computer-use/errors.js +76 -0
- package/dist/computer-use/errors.js.map +1 -0
- package/dist/computer-use/index.d.ts +53 -0
- package/dist/computer-use/index.d.ts.map +1 -0
- package/dist/computer-use/index.js +51 -0
- package/dist/computer-use/index.js.map +1 -0
- package/dist/computer-use/playwright.d.ts +76 -0
- package/dist/computer-use/playwright.d.ts.map +1 -0
- package/dist/computer-use/playwright.js +270 -0
- package/dist/computer-use/playwright.js.map +1 -0
- package/dist/computer-use/tool.d.ts +154 -0
- package/dist/computer-use/tool.d.ts.map +1 -0
- package/dist/computer-use/tool.js +210 -0
- package/dist/computer-use/tool.js.map +1 -0
- package/dist/eval/fixtures.d.ts +65 -0
- package/dist/eval/fixtures.d.ts.map +1 -0
- package/dist/eval/fixtures.js +110 -0
- package/dist/eval/fixtures.js.map +1 -0
- package/dist/eval/html-reporter.d.ts +25 -0
- package/dist/eval/html-reporter.d.ts.map +1 -0
- package/dist/eval/html-reporter.js +209 -0
- package/dist/eval/html-reporter.js.map +1 -0
- package/dist/eval/index.d.ts +271 -0
- package/dist/eval/index.d.ts.map +1 -0
- package/dist/eval/index.js +510 -0
- package/dist/eval/index.js.map +1 -0
- package/dist/eval/json-reporter.d.ts +43 -0
- package/dist/eval/json-reporter.d.ts.map +1 -0
- package/dist/eval/json-reporter.js +40 -0
- package/dist/eval/json-reporter.js.map +1 -0
- package/dist/fake.d.ts +36 -1
- package/dist/fake.d.ts.map +1 -1
- package/dist/fake.js +49 -2
- package/dist/fake.js.map +1 -1
- package/dist/file-search.d.ts +168 -0
- package/dist/file-search.d.ts.map +1 -0
- package/dist/file-search.js +158 -0
- package/dist/file-search.js.map +1 -0
- package/dist/index.d.ts +22 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/client-tools.d.ts +39 -0
- package/dist/mcp/client-tools.d.ts.map +1 -0
- package/dist/mcp/client-tools.js +147 -0
- package/dist/mcp/client-tools.js.map +1 -0
- package/dist/mcp/index.d.ts +16 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +15 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server-from-agent.d.ts +24 -0
- package/dist/mcp/server-from-agent.d.ts.map +1 -0
- package/dist/mcp/server-from-agent.js +113 -0
- package/dist/mcp/server-from-agent.js.map +1 -0
- package/dist/mcp/types.d.ts +64 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +6 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/memory-embedding/index.d.ts +121 -0
- package/dist/memory-embedding/index.d.ts.map +1 -0
- package/dist/memory-embedding/index.js +229 -0
- package/dist/memory-embedding/index.js.map +1 -0
- package/dist/memory-extract.d.ts +60 -0
- package/dist/memory-extract.d.ts.map +1 -0
- package/dist/memory-extract.js +163 -0
- package/dist/memory-extract.js.map +1 -0
- package/dist/memory-inject.d.ts +39 -0
- package/dist/memory-inject.d.ts.map +1 -0
- package/dist/memory-inject.js +135 -0
- package/dist/memory-inject.js.map +1 -0
- package/dist/memory-orm/index.d.ts +118 -0
- package/dist/memory-orm/index.d.ts.map +1 -0
- package/dist/memory-orm/index.js +187 -0
- package/dist/memory-orm/index.js.map +1 -0
- package/dist/memory.d.ts +55 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +132 -0
- package/dist/memory.js.map +1 -0
- package/dist/observers.d.ts +22 -0
- package/dist/observers.d.ts.map +1 -1
- package/dist/observers.js.map +1 -1
- package/dist/provider-tools.d.ts +15 -1
- package/dist/provider-tools.d.ts.map +1 -1
- package/dist/provider-tools.js +21 -1
- package/dist/provider-tools.js.map +1 -1
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +61 -6
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/elevenlabs.d.ts +98 -0
- package/dist/providers/elevenlabs.d.ts.map +1 -0
- package/dist/providers/elevenlabs.js +229 -0
- package/dist/providers/elevenlabs.js.map +1 -0
- package/dist/providers/google.d.ts +83 -1
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/google.js +491 -8
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/openai.d.ts +3 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +209 -5
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/voyage.d.ts +91 -0
- package/dist/providers/voyage.d.ts.map +1 -0
- package/dist/providers/voyage.js +166 -0
- package/dist/providers/voyage.js.map +1 -0
- package/dist/queue-job.d.ts +69 -4
- package/dist/queue-job.d.ts.map +1 -1
- package/dist/queue-job.js +114 -11
- package/dist/queue-job.js.map +1 -1
- package/dist/registry.d.ts +3 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +10 -0
- package/dist/registry.js.map +1 -1
- package/dist/server/provider.d.ts.map +1 -1
- package/dist/server/provider.js +23 -1
- package/dist/server/provider.js.map +1 -1
- package/dist/similarity-search.d.ts +163 -0
- package/dist/similarity-search.d.ts.map +1 -0
- package/dist/similarity-search.js +147 -0
- package/dist/similarity-search.js.map +1 -0
- package/dist/tool.d.ts.map +1 -1
- package/dist/tool.js +13 -4
- package/dist/tool.js.map +1 -1
- package/dist/types.d.ts +246 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/vector-stores/index.d.ts +96 -0
- package/dist/vector-stores/index.d.ts.map +1 -0
- package/dist/vector-stores/index.js +153 -0
- package/dist/vector-stores/index.js.map +1 -0
- package/package.json +41 -3
|
@@ -0,0 +1,121 @@
|
|
|
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 '../types.js';
|
|
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;
|
|
121
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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,aAAa,CAAA;AAEpB,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"}
|
|
@@ -0,0 +1,229 @@
|
|
|
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 '../facade.js';
|
|
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('[RudderJS AI] 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
|
+
}
|
|
229
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/memory-embedding/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { AiMiddleware, MemoryEntry, RemembersSpec } from './types.js';
|
|
2
|
+
import type { UserMemoryLookup } from './memory.js';
|
|
3
|
+
export interface MemoryExtractOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Override the {@link UserMemory} lookup. Defaults to the module-level
|
|
6
|
+
* `resolveUserMemory()` registry that `AiProvider` writes to from
|
|
7
|
+
* `AiConfig.memory`. Tests pass a closure to inject a fake.
|
|
8
|
+
*/
|
|
9
|
+
lookup?: UserMemoryLookup;
|
|
10
|
+
/**
|
|
11
|
+
* Confidence floor for the small model's self-rated `score`. Facts
|
|
12
|
+
* with a score < threshold are dropped before any `remember()` call.
|
|
13
|
+
* Default `0.7`.
|
|
14
|
+
*
|
|
15
|
+
* **Tuning note (poisoning mitigation):** the threshold is the v1
|
|
16
|
+
* defense against a malicious user planting adversarial "facts." A
|
|
17
|
+
* low threshold accepts more spam; a high threshold filters useful
|
|
18
|
+
* signal. Pair with `MemoryExtractOptions.onExtracted` for an audit
|
|
19
|
+
* log when you ship extract to production.
|
|
20
|
+
*/
|
|
21
|
+
threshold?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Called after a successful extract with the entries that survived
|
|
24
|
+
* the threshold filter and were written to the store. Use this to
|
|
25
|
+
* stream entries into telescope, an audit log, or test assertions.
|
|
26
|
+
*/
|
|
27
|
+
onExtracted?: (entries: MemoryEntry[]) => void;
|
|
28
|
+
/**
|
|
29
|
+
* Called when extract fails — small-model network error, JSON parse
|
|
30
|
+
* failure, schema-validation rejection, or `mem.remember()` throw.
|
|
31
|
+
* Errors are otherwise swallowed; the parent run never breaks.
|
|
32
|
+
*/
|
|
33
|
+
onError?: (err: unknown) => void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Post-conversation {@link AiMiddleware} that asks a small model to
|
|
37
|
+
* distill the latest `[user, assistant]` turn into durable facts and
|
|
38
|
+
* writes the survivors (above `threshold`) to the registered
|
|
39
|
+
* {@link UserMemory}. Auto-installed by `Agent.prompt` /
|
|
40
|
+
* `Agent.stream` when `Agent.remembers().extract === 'auto'` and
|
|
41
|
+
* `extractWith` is set; can also be dropped into `Agent.middleware()`
|
|
42
|
+
* manually.
|
|
43
|
+
*
|
|
44
|
+
* Runs in `onFinish` — only fires on a successful loop, so failed
|
|
45
|
+
* runs don't pollute memory. Failures inside the extract path
|
|
46
|
+
* (network, JSON parse, zod validation, `remember()` throw) are
|
|
47
|
+
* routed through `MemoryExtractOptions.onError` and otherwise
|
|
48
|
+
* swallowed; the parent prompt never breaks because of memory work.
|
|
49
|
+
*
|
|
50
|
+
* **Auto-installed extracts skip continuation calls** (`options.messages`
|
|
51
|
+
* set) at the host level — the same way auto-inject does. Manually
|
|
52
|
+
* installed extracts always run.
|
|
53
|
+
*
|
|
54
|
+
* **Pitfall — memory poisoning:** auto-extraction lets a malicious
|
|
55
|
+
* user plant adversarial "facts." The threshold (default 0.7) is the
|
|
56
|
+
* baseline defense; pair with `onExtracted` for an audit log when you
|
|
57
|
+
* ship to production. A content-filter middleware is a follow-up.
|
|
58
|
+
*/
|
|
59
|
+
export declare function withMemoryExtract(spec: RemembersSpec, opts?: MemoryExtractOptions): AiMiddleware;
|
|
60
|
+
//# sourceMappingURL=memory-extract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-extract.d.ts","sourceRoot":"","sources":["../src/memory-extract.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEV,YAAY,EAEZ,WAAW,EACX,aAAa,EACd,MAAM,YAAY,CAAA;AACnB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAEnD,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,MAAM,CAAC,EAAO,gBAAgB,CAAA;IAC9B;;;;;;;;;;OAUG;IACH,SAAS,CAAC,EAAI,MAAM,CAAA;IACpB;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,IAAI,CAAA;IAC9C;;;;OAIG;IACH,OAAO,CAAC,EAAM,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;CACrC;AAuBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,aAAa,EACnB,IAAI,GAAE,oBAAyB,GAC9B,YAAY,CAwDd"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { agent, resolveUserMemory } from './agent.js';
|
|
3
|
+
import { Output } from './output.js';
|
|
4
|
+
/**
|
|
5
|
+
* The shape we ask the small model to fill in when distilling facts.
|
|
6
|
+
* `score` is the model's self-rated confidence in [0, 1]; tags are
|
|
7
|
+
* additive — the spec's `tags` are unioned in regardless.
|
|
8
|
+
*/
|
|
9
|
+
const ExtractedFactsSchema = z.object({
|
|
10
|
+
facts: z.array(z.object({
|
|
11
|
+
fact: z.string().min(1),
|
|
12
|
+
score: z.number().min(0).max(1),
|
|
13
|
+
tags: z.array(z.string()).optional(),
|
|
14
|
+
})),
|
|
15
|
+
});
|
|
16
|
+
const EXTRACT_INSTRUCTIONS = [
|
|
17
|
+
'You distill durable facts about a USER from a single conversation turn.',
|
|
18
|
+
'A "durable fact" is something true about the user that future conversations would benefit from knowing — preferences, identifying details, ongoing projects, persistent constraints.',
|
|
19
|
+
'Skip anything specific to this conversation, ephemeral state, or already-obvious context.',
|
|
20
|
+
'Self-rate each fact\'s confidence in [0, 1]; the host filters out anything below the threshold.',
|
|
21
|
+
'If nothing is worth remembering, return {"facts": []}.',
|
|
22
|
+
].join(' ');
|
|
23
|
+
/**
|
|
24
|
+
* Post-conversation {@link AiMiddleware} that asks a small model to
|
|
25
|
+
* distill the latest `[user, assistant]` turn into durable facts and
|
|
26
|
+
* writes the survivors (above `threshold`) to the registered
|
|
27
|
+
* {@link UserMemory}. Auto-installed by `Agent.prompt` /
|
|
28
|
+
* `Agent.stream` when `Agent.remembers().extract === 'auto'` and
|
|
29
|
+
* `extractWith` is set; can also be dropped into `Agent.middleware()`
|
|
30
|
+
* manually.
|
|
31
|
+
*
|
|
32
|
+
* Runs in `onFinish` — only fires on a successful loop, so failed
|
|
33
|
+
* runs don't pollute memory. Failures inside the extract path
|
|
34
|
+
* (network, JSON parse, zod validation, `remember()` throw) are
|
|
35
|
+
* routed through `MemoryExtractOptions.onError` and otherwise
|
|
36
|
+
* swallowed; the parent prompt never breaks because of memory work.
|
|
37
|
+
*
|
|
38
|
+
* **Auto-installed extracts skip continuation calls** (`options.messages`
|
|
39
|
+
* set) at the host level — the same way auto-inject does. Manually
|
|
40
|
+
* installed extracts always run.
|
|
41
|
+
*
|
|
42
|
+
* **Pitfall — memory poisoning:** auto-extraction lets a malicious
|
|
43
|
+
* user plant adversarial "facts." The threshold (default 0.7) is the
|
|
44
|
+
* baseline defense; pair with `onExtracted` for an audit log when you
|
|
45
|
+
* ship to production. A content-filter middleware is a follow-up.
|
|
46
|
+
*/
|
|
47
|
+
export function withMemoryExtract(spec, opts = {}) {
|
|
48
|
+
const lookup = opts.lookup ?? resolveUserMemory;
|
|
49
|
+
const threshold = opts.threshold ?? 0.7;
|
|
50
|
+
const wrapper = Output.object({ schema: ExtractedFactsSchema });
|
|
51
|
+
return {
|
|
52
|
+
name: 'memory-extract',
|
|
53
|
+
async onFinish(ctx) {
|
|
54
|
+
try {
|
|
55
|
+
if (spec.extract !== 'auto')
|
|
56
|
+
return;
|
|
57
|
+
if (!spec.extractWith)
|
|
58
|
+
return;
|
|
59
|
+
const mem = lookup();
|
|
60
|
+
if (!mem)
|
|
61
|
+
return;
|
|
62
|
+
const turn = extractLatestTurn(ctx.messages);
|
|
63
|
+
if (!turn)
|
|
64
|
+
return;
|
|
65
|
+
const extractor = agent({
|
|
66
|
+
instructions: `${EXTRACT_INSTRUCTIONS}\n\n${wrapper.toSystemPrompt()}`,
|
|
67
|
+
model: spec.extractWith,
|
|
68
|
+
});
|
|
69
|
+
const prompt = [
|
|
70
|
+
`User said: ${JSON.stringify(turn.user)}`,
|
|
71
|
+
`Assistant replied: ${JSON.stringify(turn.assistant)}`,
|
|
72
|
+
'',
|
|
73
|
+
'Extract durable facts about the USER from the above. Return strictly valid JSON.',
|
|
74
|
+
].join('\n');
|
|
75
|
+
const response = await extractor.prompt(prompt);
|
|
76
|
+
const parsed = wrapper.parse(response.text);
|
|
77
|
+
const surviving = parsed.facts.filter(f => f.score >= threshold);
|
|
78
|
+
if (surviving.length === 0) {
|
|
79
|
+
opts.onExtracted?.([]);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const tagsFromSpec = spec.tags ?? [];
|
|
83
|
+
const written = [];
|
|
84
|
+
for (const f of surviving) {
|
|
85
|
+
const merged = mergeTags(f.tags, tagsFromSpec);
|
|
86
|
+
const entry = await mem.remember(spec.user, f.fact, {
|
|
87
|
+
score: f.score,
|
|
88
|
+
...(merged.length > 0 ? { tags: merged } : {}),
|
|
89
|
+
});
|
|
90
|
+
written.push(entry);
|
|
91
|
+
}
|
|
92
|
+
opts.onExtracted?.(written);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
opts.onError?.(err);
|
|
96
|
+
// Never break the parent run.
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
// ─── Helpers ──────────────────────────────────────────────
|
|
102
|
+
/**
|
|
103
|
+
* Walk `messages` from the end and return the most recent
|
|
104
|
+
* `(user, assistant)` pair where the assistant message follows the
|
|
105
|
+
* user message. Skips trailing `tool` messages so multi-step tool
|
|
106
|
+
* loops still surface the original user request and the model's
|
|
107
|
+
* final synthesis.
|
|
108
|
+
*
|
|
109
|
+
* Returns `null` when:
|
|
110
|
+
* - the loop didn't reach a final assistant message (stopped on a
|
|
111
|
+
* client-tool pause, approval gate, or handoff), or
|
|
112
|
+
* - the assistant message has no extractable text content.
|
|
113
|
+
*/
|
|
114
|
+
function extractLatestTurn(messages) {
|
|
115
|
+
let assistantText = null;
|
|
116
|
+
let lastAssistantIdx = -1;
|
|
117
|
+
// Walk backwards looking for the LAST assistant message that's a
|
|
118
|
+
// text reply (not a tool-calls message).
|
|
119
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
120
|
+
const m = messages[i];
|
|
121
|
+
if (!m || m.role !== 'assistant')
|
|
122
|
+
continue;
|
|
123
|
+
if (m.toolCalls && m.toolCalls.length > 0)
|
|
124
|
+
continue; // tool-call step, not a final reply
|
|
125
|
+
const text = contentToString(m.content);
|
|
126
|
+
if (text.length === 0)
|
|
127
|
+
continue;
|
|
128
|
+
assistantText = text;
|
|
129
|
+
lastAssistantIdx = i;
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
if (assistantText === null || lastAssistantIdx === -1)
|
|
133
|
+
return null;
|
|
134
|
+
// Find the most recent user message before that assistant message.
|
|
135
|
+
for (let i = lastAssistantIdx - 1; i >= 0; i--) {
|
|
136
|
+
const m = messages[i];
|
|
137
|
+
if (!m || m.role !== 'user')
|
|
138
|
+
continue;
|
|
139
|
+
const text = contentToString(m.content);
|
|
140
|
+
if (text.length === 0)
|
|
141
|
+
continue;
|
|
142
|
+
return { user: text, assistant: assistantText };
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
function contentToString(content) {
|
|
147
|
+
if (typeof content === 'string')
|
|
148
|
+
return content;
|
|
149
|
+
const out = [];
|
|
150
|
+
for (const p of content) {
|
|
151
|
+
if (p.type === 'text' && typeof p.text === 'string')
|
|
152
|
+
out.push(p.text);
|
|
153
|
+
}
|
|
154
|
+
return out.join('\n');
|
|
155
|
+
}
|
|
156
|
+
function mergeTags(modelTags, specTags) {
|
|
157
|
+
if (!modelTags || modelTags.length === 0)
|
|
158
|
+
return [...specTags];
|
|
159
|
+
if (specTags.length === 0)
|
|
160
|
+
return [...modelTags];
|
|
161
|
+
return Array.from(new Set([...modelTags, ...specTags]));
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=memory-extract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-extract.js","sourceRoot":"","sources":["../src/memory-extract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AA2CpC;;;;GAIG;AACH,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;QACtB,IAAI,EAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACxB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/B,IAAI,EAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;KACtC,CAAC,CAAC;CACJ,CAAC,CAAA;AAEF,MAAM,oBAAoB,GAAG;IAC3B,yEAAyE;IACzE,sLAAsL;IACtL,2FAA2F;IAC3F,iGAAiG;IACjG,wDAAwD;CACzD,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAEX;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,iBAAiB,CAC/B,IAAmB,EACnB,OAA6B,EAAE;IAE/B,MAAM,MAAM,GAAM,IAAI,CAAC,MAAM,IAAO,iBAAiB,CAAA;IACrD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,GAAG,CAAA;IACvC,MAAM,OAAO,GAAK,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC,CAAA;IAEjE,OAAO;QACL,IAAI,EAAE,gBAAgB;QACtB,KAAK,CAAC,QAAQ,CAAC,GAAG;YAChB,IAAI,CAAC;gBACH,IAAI,IAAI,CAAC,OAAO,KAAK,MAAM;oBAAE,OAAM;gBACnC,IAAI,CAAC,IAAI,CAAC,WAAW;oBAAE,OAAM;gBAE7B,MAAM,GAAG,GAAG,MAAM,EAAE,CAAA;gBACpB,IAAI,CAAC,GAAG;oBAAE,OAAM;gBAEhB,MAAM,IAAI,GAAG,iBAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;gBAC5C,IAAI,CAAC,IAAI;oBAAE,OAAM;gBAEjB,MAAM,SAAS,GAAG,KAAK,CAAC;oBACtB,YAAY,EAAE,GAAG,oBAAoB,OAAO,OAAO,CAAC,cAAc,EAAE,EAAE;oBACtE,KAAK,EAAS,IAAI,CAAC,WAAW;iBAC/B,CAAC,CAAA;gBAEF,MAAM,MAAM,GAAG;oBACb,cAAc,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;oBACzC,sBAAsB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE;oBACtD,EAAE;oBACF,kFAAkF;iBACnF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBAEZ,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;gBAC/C,MAAM,MAAM,GAAK,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;gBAE7C,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,SAAS,CAAC,CAAA;gBAChE,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBAC3B,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC,CAAA;oBACtB,OAAM;gBACR,CAAC;gBAED,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,IAAI,EAAE,CAAA;gBACpC,MAAM,OAAO,GAAkB,EAAE,CAAA;gBACjC,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;oBAC1B,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;oBAC9C,MAAM,KAAK,GAAI,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;wBACnD,KAAK,EAAE,CAAC,CAAC,KAAK;wBACd,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;qBAC/C,CAAC,CAAA;oBACF,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;gBACrB,CAAC;gBACD,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,CAAA;YAC7B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAA;gBACnB,8BAA8B;YAChC,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC;AAED,6DAA6D;AAE7D;;;;;;;;;;;GAWG;AACH,SAAS,iBAAiB,CAAC,QAAqB;IAC9C,IAAI,aAAa,GAAkB,IAAI,CAAA;IACvC,IAAI,gBAAgB,GAAG,CAAC,CAAC,CAAA;IAEzB,iEAAiE;IACjE,yCAAyC;IACzC,KAAK,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9C,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;QACrB,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW;YAAE,SAAQ;QAC1C,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;YAAE,SAAQ,CAAG,oCAAoC;QAC1F,MAAM,IAAI,GAAG,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;QACvC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,SAAQ;QAC/B,aAAa,GAAI,IAAI,CAAA;QACrB,gBAAgB,GAAG,CAAC,CAAA;QACpB,MAAK;IACP,CAAC;IACD,IAAI,aAAa,KAAK,IAAI,IAAI,gBAAgB,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAA;IAElE,mEAAmE;IACnE,KAAK,IAAI,CAAC,GAAG,gBAAgB,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/C,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;QACrB,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM;YAAE,SAAQ;QACrC,MAAM,IAAI,GAAG,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;QACvC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,SAAQ;QAC/B,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE,CAAA;IACjD,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAS,eAAe,CAAC,OAA+B;IACtD,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAA;IAC/C,MAAM,GAAG,GAAa,EAAE,CAAA;IACxB,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ;YAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;IACvE,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACvB,CAAC;AAED,SAAS,SAAS,CAAC,SAA+B,EAAE,QAAkB;IACpE,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAA;IAC9D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAkB,OAAO,CAAC,GAAG,SAAS,CAAC,CAAA;IAChE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,SAAS,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;AACzD,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { AiMiddleware, RemembersSpec } from './types.js';
|
|
2
|
+
import type { UserMemoryLookup } from './memory.js';
|
|
3
|
+
export interface MemoryInjectOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Override the {@link UserMemory} lookup. Defaults to the module-level
|
|
6
|
+
* `resolveUserMemory()` registry that `AiProvider` writes to from
|
|
7
|
+
* `AiConfig.memory`. Tests pass a closure to inject a fake.
|
|
8
|
+
*/
|
|
9
|
+
lookup?: UserMemoryLookup;
|
|
10
|
+
/**
|
|
11
|
+
* Approximate-tokens estimator used for `injectTokenBudget`. Defaults
|
|
12
|
+
* to `Math.ceil(text.length / 4)` — fine for English-heavy facts; pass
|
|
13
|
+
* a tiktoken-backed estimator if you need accuracy.
|
|
14
|
+
*/
|
|
15
|
+
estimateTokens?: (text: string) => number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Pre-prompt {@link AiMiddleware} that consults a {@link UserMemory}, picks
|
|
19
|
+
* facts relevant to the latest user input, and prepends them to the
|
|
20
|
+
* agent's system message as a fenced `<user-memory>…</user-memory>`
|
|
21
|
+
* block. Auto-installed by `Agent.prompt` / `Agent.stream` when
|
|
22
|
+
* `Agent.remembers()` returns `{ inject: 'auto', … }`; can also be
|
|
23
|
+
* dropped into `Agent.middleware()` manually.
|
|
24
|
+
*
|
|
25
|
+
* Runs in `onStart` (async) — `onConfig` is sync and `recall()` is not.
|
|
26
|
+
* Mutates `ctx.messages[0]` (the system message) in place; the agent
|
|
27
|
+
* loop's `messages` array is the same reference, so the model sees the
|
|
28
|
+
* augmented prompt on the very next provider call.
|
|
29
|
+
*
|
|
30
|
+
* Skips silently when:
|
|
31
|
+
* - no `UserMemory` is registered (lookup returns `undefined`)
|
|
32
|
+
* - no user message exists in `ctx.messages` (continuation flow where
|
|
33
|
+
* the trailing message is `tool` / `assistant`)
|
|
34
|
+
* - `recall()` returns no facts
|
|
35
|
+
* - the rendered block doesn't fit even one fact under
|
|
36
|
+
* `spec.injectTokenBudget`
|
|
37
|
+
*/
|
|
38
|
+
export declare function withMemoryInject(spec: RemembersSpec, opts?: MemoryInjectOptions): AiMiddleware;
|
|
39
|
+
//# sourceMappingURL=memory-inject.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-inject.d.ts","sourceRoot":"","sources":["../src/memory-inject.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,YAAY,EAGZ,aAAa,EACd,MAAM,YAAY,CAAA;AACnB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAEnD,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,MAAM,CAAC,EAAU,gBAAgB,CAAA;IACjC;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;CAC1C;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,aAAa,EACnB,IAAI,GAAE,mBAAwB,GAC7B,YAAY,CA+Bd"}
|