@batonfx/memory 0.3.3 → 0.3.5
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/.turbo/turbo-build.log +1 -1
- package/dist/index.js +31 -5
- package/package.json +2 -2
- package/src/semantic-recall.ts +1 -0
- package/src/vector-store.ts +14 -0
- package/src/working-memory.ts +16 -0
- package/test/semantic-recall.test.ts +33 -0
- package/test/vector-store.test.ts +39 -0
- package/test/working-memory.test.ts +54 -0
package/.turbo/turbo-build.log
CHANGED
package/dist/index.js
CHANGED
|
@@ -32662,11 +32662,13 @@ class Memory extends exports_Context.Service()("@batonfx/core/Memory") {
|
|
|
32662
32662
|
}
|
|
32663
32663
|
var noop = {
|
|
32664
32664
|
recall: () => exports_Effect.succeed([]),
|
|
32665
|
-
remember: () => exports_Effect.void
|
|
32665
|
+
remember: () => exports_Effect.void,
|
|
32666
|
+
forget: () => exports_Effect.void
|
|
32666
32667
|
};
|
|
32667
32668
|
var merge6 = (first, second) => ({
|
|
32668
32669
|
recall: (input) => exports_Effect.all([first.recall(input), second.recall(input)]).pipe(exports_Effect.map(([firstItems, secondItems]) => [...firstItems, ...secondItems])),
|
|
32669
|
-
remember: (input) => exports_Effect.all([first.remember(input), second.remember(input)], { discard: true })
|
|
32670
|
+
remember: (input) => exports_Effect.all([first.remember(input), second.remember(input)], { discard: true }),
|
|
32671
|
+
forget: (input) => exports_Effect.all([first.forget(input), second.forget(input)], { discard: true })
|
|
32670
32672
|
});
|
|
32671
32673
|
var noopLayer = exports_Layer.succeed(Memory, Memory.of(noop));
|
|
32672
32674
|
var testLayer = (implementation) => exports_Layer.succeed(Memory, Memory.of(implementation));
|
|
@@ -32862,7 +32864,12 @@ var make37 = exports_Ref.make(exports_HashMap.empty()).pipe(exports_Effect.map((
|
|
|
32862
32864
|
matches.push({ document, score });
|
|
32863
32865
|
}
|
|
32864
32866
|
return matches.toSorted((left, right) => right.score - left.score).slice(0, input.limit);
|
|
32865
|
-
})
|
|
32867
|
+
}),
|
|
32868
|
+
delete: (input) => exports_Ref.update(documents, (current) => exports_HashMap.filter(current, (document) => {
|
|
32869
|
+
if (!sameKey(document.key, input.key))
|
|
32870
|
+
return true;
|
|
32871
|
+
return input.id === undefined ? false : document.id !== input.id;
|
|
32872
|
+
}))
|
|
32866
32873
|
})));
|
|
32867
32874
|
var memoryLayer2 = exports_Layer.effect(VectorStore, make37.pipe(exports_Effect.map(VectorStore.of)));
|
|
32868
32875
|
var testLayer2 = (implementation) => exports_Layer.succeed(VectorStore, VectorStore.of(implementation));
|
|
@@ -32939,7 +32946,8 @@ var make38 = (options = {}) => exports_Effect.gen(function* () {
|
|
|
32939
32946
|
embedding: embedding.vector
|
|
32940
32947
|
}
|
|
32941
32948
|
])))), exports_Effect.mapError(memoryError));
|
|
32942
|
-
}
|
|
32949
|
+
},
|
|
32950
|
+
forget: (input) => store.delete(input).pipe(exports_Effect.mapError(memoryError))
|
|
32943
32951
|
};
|
|
32944
32952
|
});
|
|
32945
32953
|
var makeSemanticRecall = make38;
|
|
@@ -33064,7 +33072,25 @@ var make39 = (options = {}) => exports_Ref.make(exports_HashMap.empty()).pipe(ex
|
|
|
33064
33072
|
};
|
|
33065
33073
|
yield* exports_Ref.update(states, exports_HashMap.set(id2, nextState));
|
|
33066
33074
|
});
|
|
33067
|
-
}
|
|
33075
|
+
},
|
|
33076
|
+
forget: (input) => exports_Ref.update(states, (current) => {
|
|
33077
|
+
const id2 = keyId(input.key);
|
|
33078
|
+
if (input.id === undefined)
|
|
33079
|
+
return exports_HashMap.remove(current, id2);
|
|
33080
|
+
const existing = exports_HashMap.get(current, id2);
|
|
33081
|
+
if (existing._tag === "None")
|
|
33082
|
+
return current;
|
|
33083
|
+
const summary = input.id === "working-summary" ? undefined : existing.value.summary;
|
|
33084
|
+
const recent = existing.value.recent.filter((item) => item.id !== input.id);
|
|
33085
|
+
if (summary === undefined && recent.length === 0)
|
|
33086
|
+
return exports_HashMap.remove(current, id2);
|
|
33087
|
+
const nextState = {
|
|
33088
|
+
recent,
|
|
33089
|
+
counter: existing.value.counter,
|
|
33090
|
+
...summary === undefined ? {} : { summary }
|
|
33091
|
+
};
|
|
33092
|
+
return exports_HashMap.set(current, id2, nextState);
|
|
33093
|
+
})
|
|
33068
33094
|
};
|
|
33069
33095
|
}));
|
|
33070
33096
|
var makeWorkingMemory = make39;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@batonfx/memory",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.5",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
7
7
|
"exports": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"typecheck": "bun tsc --noEmit"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@batonfx/core": "0.3.
|
|
17
|
+
"@batonfx/core": "0.3.5",
|
|
18
18
|
"effect": "4.0.0-beta.93"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
package/src/semantic-recall.ts
CHANGED
package/src/vector-store.ts
CHANGED
|
@@ -28,6 +28,12 @@ export interface Query {
|
|
|
28
28
|
readonly minScore?: number
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/** @experimental */
|
|
32
|
+
export interface DeleteInput {
|
|
33
|
+
readonly key: Memory.Key
|
|
34
|
+
readonly id?: string | undefined
|
|
35
|
+
}
|
|
36
|
+
|
|
31
37
|
/** @experimental */
|
|
32
38
|
export class VectorStoreError extends Schema.TaggedErrorClass<VectorStoreError>()("@batonfx/memory/VectorStoreError", {
|
|
33
39
|
message: Schema.String,
|
|
@@ -37,6 +43,7 @@ export class VectorStoreError extends Schema.TaggedErrorClass<VectorStoreError>(
|
|
|
37
43
|
export interface Interface {
|
|
38
44
|
readonly upsert: (documents: ReadonlyArray<Embedded>) => Effect.Effect<void, VectorStoreError>
|
|
39
45
|
readonly query: (query: Query) => Effect.Effect<ReadonlyArray<Match>, VectorStoreError>
|
|
46
|
+
readonly delete: (input: DeleteInput) => Effect.Effect<void, VectorStoreError>
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
/** @experimental */
|
|
@@ -106,6 +113,13 @@ const make = Ref.make(HashMap.empty<string, Embedded>()).pipe(
|
|
|
106
113
|
}
|
|
107
114
|
return matches.toSorted((left, right) => right.score - left.score).slice(0, input.limit)
|
|
108
115
|
}),
|
|
116
|
+
delete: (input) =>
|
|
117
|
+
Ref.update(documents, (current) =>
|
|
118
|
+
HashMap.filter(current, (document) => {
|
|
119
|
+
if (!sameKey(document.key, input.key)) return true
|
|
120
|
+
return input.id === undefined ? false : document.id !== input.id
|
|
121
|
+
}),
|
|
122
|
+
),
|
|
109
123
|
}),
|
|
110
124
|
),
|
|
111
125
|
)
|
package/src/working-memory.ts
CHANGED
|
@@ -180,6 +180,22 @@ export const make = (options: Options = {}): Effect.Effect<Memory.Interface> =>
|
|
|
180
180
|
yield* Ref.update(states, HashMap.set(id, nextState))
|
|
181
181
|
})
|
|
182
182
|
},
|
|
183
|
+
forget: (input) =>
|
|
184
|
+
Ref.update(states, (current) => {
|
|
185
|
+
const id = keyId(input.key)
|
|
186
|
+
if (input.id === undefined) return HashMap.remove(current, id)
|
|
187
|
+
const existing = HashMap.get(current, id)
|
|
188
|
+
if (existing._tag === "None") return current
|
|
189
|
+
const summary = input.id === "working-summary" ? undefined : existing.value.summary
|
|
190
|
+
const recent = existing.value.recent.filter((item) => item.id !== input.id)
|
|
191
|
+
if (summary === undefined && recent.length === 0) return HashMap.remove(current, id)
|
|
192
|
+
const nextState: KeyState = {
|
|
193
|
+
recent,
|
|
194
|
+
counter: existing.value.counter,
|
|
195
|
+
...(summary === undefined ? {} : { summary }),
|
|
196
|
+
}
|
|
197
|
+
return HashMap.set(current, id, nextState)
|
|
198
|
+
}),
|
|
183
199
|
}
|
|
184
200
|
}),
|
|
185
201
|
)
|
|
@@ -91,6 +91,39 @@ describe("SemanticRecall", () => {
|
|
|
91
91
|
}).pipe(Effect.provide(memoryLayer)),
|
|
92
92
|
)
|
|
93
93
|
|
|
94
|
+
it.effect("forgets one semantic memory id within the exact memory key", () =>
|
|
95
|
+
Effect.gen(function* () {
|
|
96
|
+
const memory = yield* Memory.Memory
|
|
97
|
+
|
|
98
|
+
yield* memory.remember({
|
|
99
|
+
key,
|
|
100
|
+
turn: 0,
|
|
101
|
+
terminal: true,
|
|
102
|
+
transcript: prompt(user("What color is the sky?"), assistant("blue")),
|
|
103
|
+
})
|
|
104
|
+
yield* memory.remember({
|
|
105
|
+
key,
|
|
106
|
+
turn: 1,
|
|
107
|
+
terminal: true,
|
|
108
|
+
transcript: prompt(user("What color is the ocean?"), assistant("blue")),
|
|
109
|
+
})
|
|
110
|
+
yield* memory.remember({
|
|
111
|
+
key: otherKey,
|
|
112
|
+
turn: 0,
|
|
113
|
+
terminal: true,
|
|
114
|
+
transcript: prompt(user("What color is the door?"), assistant("blue")),
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
yield* memory.forget({ key, id: "semantic-1" })
|
|
118
|
+
|
|
119
|
+
const retained = yield* memory.recall({ key, turn: 0, prompt: prompt(user("color")) })
|
|
120
|
+
const otherRetained = yield* memory.recall({ key: otherKey, turn: 0, prompt: prompt(user("color")) })
|
|
121
|
+
|
|
122
|
+
expect(retained.map(itemText)).toEqual(["User: What color is the ocean?\nAssistant: blue"])
|
|
123
|
+
expect(otherRetained.map(itemText)).toEqual(["User: What color is the door?\nAssistant: blue"])
|
|
124
|
+
}).pipe(Effect.provide(memoryLayer)),
|
|
125
|
+
)
|
|
126
|
+
|
|
94
127
|
it.effect("maps embedding failures to MemoryError", () => {
|
|
95
128
|
const embeddingError = AiError.make({
|
|
96
129
|
module: "SemanticRecallTest",
|
|
@@ -79,4 +79,43 @@ describe("VectorStore", () => {
|
|
|
79
79
|
expect(failure.message).toContain("dimension")
|
|
80
80
|
}).pipe(Effect.provide(VectorStore.memoryLayer)),
|
|
81
81
|
)
|
|
82
|
+
|
|
83
|
+
it.effect("deletes documents for the exact memory key", () =>
|
|
84
|
+
Effect.gen(function* () {
|
|
85
|
+
const store = yield* VectorStore.VectorStore
|
|
86
|
+
|
|
87
|
+
yield* store.upsert([
|
|
88
|
+
document("same-id", key, "visible", [1, 0]),
|
|
89
|
+
document("same-id", otherSubject, "other subject", [1, 0]),
|
|
90
|
+
])
|
|
91
|
+
|
|
92
|
+
yield* store.delete({ key })
|
|
93
|
+
|
|
94
|
+
const deletedMatches = yield* store.query({ key, embedding: [1, 0], limit: 10 })
|
|
95
|
+
const remainingMatches = yield* store.query({ key: otherSubject, embedding: [1, 0], limit: 10 })
|
|
96
|
+
|
|
97
|
+
expect(deletedMatches).toEqual([])
|
|
98
|
+
expect(remainingMatches.map((match) => match.document.text)).toEqual(["other subject"])
|
|
99
|
+
}).pipe(Effect.provide(VectorStore.memoryLayer)),
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
it.effect("deletes one document id within the exact memory key", () =>
|
|
103
|
+
Effect.gen(function* () {
|
|
104
|
+
const store = yield* VectorStore.VectorStore
|
|
105
|
+
|
|
106
|
+
yield* store.upsert([
|
|
107
|
+
document("first", key, "first", [1, 0]),
|
|
108
|
+
document("second", key, "second", [0, 1]),
|
|
109
|
+
document("first", otherSubject, "other subject", [1, 0]),
|
|
110
|
+
])
|
|
111
|
+
|
|
112
|
+
yield* store.delete({ key, id: "first" })
|
|
113
|
+
|
|
114
|
+
const deletedKeyMatches = yield* store.query({ key, embedding: [1, 0], limit: 10 })
|
|
115
|
+
const otherSubjectMatches = yield* store.query({ key: otherSubject, embedding: [1, 0], limit: 10 })
|
|
116
|
+
|
|
117
|
+
expect(deletedKeyMatches.map((match) => match.document.text)).toEqual(["second"])
|
|
118
|
+
expect(otherSubjectMatches.map((match) => match.document.text)).toEqual(["other subject"])
|
|
119
|
+
}).pipe(Effect.provide(VectorStore.memoryLayer)),
|
|
120
|
+
)
|
|
82
121
|
})
|
|
@@ -98,4 +98,58 @@ describe("WorkingMemory", () => {
|
|
|
98
98
|
expect(recalled).toEqual([])
|
|
99
99
|
}).pipe(Effect.provide(WorkingMemory.layer({ maxMessages: 2 }))),
|
|
100
100
|
)
|
|
101
|
+
|
|
102
|
+
it.effect("forgets the exact memory key", () =>
|
|
103
|
+
Effect.gen(function* () {
|
|
104
|
+
const memory = yield* Memory.Memory
|
|
105
|
+
|
|
106
|
+
yield* memory.remember({
|
|
107
|
+
key,
|
|
108
|
+
turn: 0,
|
|
109
|
+
terminal: true,
|
|
110
|
+
transcript: prompt(user("one"), assistant("two")),
|
|
111
|
+
})
|
|
112
|
+
yield* memory.remember({
|
|
113
|
+
key: otherKey,
|
|
114
|
+
turn: 0,
|
|
115
|
+
terminal: true,
|
|
116
|
+
transcript: prompt(user("three"), assistant("four")),
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
yield* memory.forget({ key })
|
|
120
|
+
|
|
121
|
+
const forgotten = yield* memory.recall({ key, turn: 0, prompt: prompt(user("current")) })
|
|
122
|
+
const retained = yield* memory.recall({ key: otherKey, turn: 0, prompt: prompt(user("current")) })
|
|
123
|
+
|
|
124
|
+
expect(forgotten).toEqual([])
|
|
125
|
+
expect(retained.map(itemText)).toEqual(["User: three", "Assistant: four"])
|
|
126
|
+
}).pipe(Effect.provide(WorkingMemory.layer({ maxMessages: 2 }))),
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
it.effect("forgets one recalled item id within the exact memory key", () =>
|
|
130
|
+
Effect.gen(function* () {
|
|
131
|
+
const memory = yield* Memory.Memory
|
|
132
|
+
|
|
133
|
+
yield* memory.remember({
|
|
134
|
+
key,
|
|
135
|
+
turn: 0,
|
|
136
|
+
terminal: true,
|
|
137
|
+
transcript: prompt(user("one"), assistant("two")),
|
|
138
|
+
})
|
|
139
|
+
yield* memory.remember({
|
|
140
|
+
key: otherKey,
|
|
141
|
+
turn: 0,
|
|
142
|
+
terminal: true,
|
|
143
|
+
transcript: prompt(user("three"), assistant("four")),
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
yield* memory.forget({ key, id: "working-1" })
|
|
147
|
+
|
|
148
|
+
const retained = yield* memory.recall({ key, turn: 0, prompt: prompt(user("current")) })
|
|
149
|
+
const otherRetained = yield* memory.recall({ key: otherKey, turn: 0, prompt: prompt(user("current")) })
|
|
150
|
+
|
|
151
|
+
expect(retained.map(itemText)).toEqual(["Assistant: two"])
|
|
152
|
+
expect(otherRetained.map(itemText)).toEqual(["User: three", "Assistant: four"])
|
|
153
|
+
}).pipe(Effect.provide(WorkingMemory.layer({ maxMessages: 2 }))),
|
|
154
|
+
)
|
|
101
155
|
})
|