@effect-agent/storage-memory 0.1.0-beta.41 → 0.1.0-beta.44

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.
@@ -50,9 +50,12 @@ interface IndexData {
50
50
 
51
51
  const sameProfile = Schema.toEquivalence(SemanticMemoryProfile);
52
52
  const sameSource = Schema.toEquivalence(MemoryIndexSource.Wire);
53
+
53
54
  const error = (operation: string, reason: MemoryIndexError["reason"]): MemoryIndexError =>
54
55
  MemoryIndexError.make({ operation, reason });
56
+
55
57
  const keyString = (key: MemoryKey): string => JSON.stringify([key.namespace.address, key.id]);
58
+
56
59
  const sourceIdentityBytes = (source: MemoryIndexSource): number =>
57
60
  Encoding.encodeHex(JSON.stringify(source)).length / 2;
58
61
 
@@ -91,10 +94,12 @@ const sourceIsFenced = (source: MemoryIndexSource, existing: StoredEntry): boole
91
94
 
92
95
  const squaredNorm = (vector: ReadonlyArray<number>): number | null => {
93
96
  let sum = 0;
97
+
94
98
  for (const value of vector) {
95
99
  sum += value * value;
96
100
  if (!Number.isFinite(sum)) return null;
97
101
  }
102
+
98
103
  return sum > 0 ? sum : null;
99
104
  };
100
105
 
@@ -108,9 +113,11 @@ const validateChunks = Effect.fn("InMemorySemanticIndex.validateChunks")(functio
108
113
  ): Effect.fn.Return<void, MemoryIndexError> {
109
114
  let nextByte = 0;
110
115
  const passageIds = new Set<string>();
116
+
111
117
  for (let index = 0; index < chunks.length; index++) {
112
118
  const chunk = chunks[index];
113
119
  const byteLength = Encoding.encodeHex(chunk.text).length / 2;
120
+
114
121
  if (
115
122
  chunk.ordinal !== index ||
116
123
  chunk.startByte !== nextByte ||
@@ -131,10 +138,12 @@ const cosine = (left: ReadonlyArray<number>, right: ReadonlyArray<number>): numb
131
138
  const leftNorm = Math.sqrt(squaredNorm(left) ?? 1);
132
139
  const rightNorm = Math.sqrt(squaredNorm(right) ?? 1);
133
140
  let score = 0;
141
+
134
142
  for (let index = 0; index < left.length; index++) {
135
143
  score += (left[index] / leftNorm) * (right[index] / rightNorm);
136
144
  }
137
145
  const bounded = Math.max(-1, Math.min(1, score));
146
+
138
147
  return bounded === 0 ? 0 : bounded;
139
148
  };
140
149
 
@@ -154,16 +163,19 @@ const makeIndex = Effect.fn("InMemorySemanticIndex.make")(function* (
154
163
  )),
155
164
  }),
156
165
  );
166
+
157
167
  const capacity = yield* decodeBoundary(
158
168
  InMemorySemanticIndexCapacity,
159
169
  rawCapacity,
160
170
  "configure semantic memory index",
161
171
  );
172
+
162
173
  if (capacity.maxChunks * profile.dimensions > MaxStoredVectorComponents) {
163
174
  return yield* error("configure semantic memory index", "invalid-input");
164
175
  }
165
176
  const maxSourceBytes = capacity.maxSourceBytes ?? 16_777_216;
166
177
  const data = yield* Ref.make<IndexData>({ closed: false, entries: new Map(), sourceBytes: 0 });
178
+
167
179
  yield* Effect.addFinalizer(() =>
168
180
  Ref.set(data, { closed: true, entries: new Map(), sourceBytes: 0 }),
169
181
  );
@@ -176,21 +188,25 @@ const makeIndex = Effect.fn("InMemorySemanticIndex.make")(function* (
176
188
  "InMemorySemanticIndex.replace",
177
189
  )(function* (rawRequest) {
178
190
  const operation = "replace semantic memory source";
191
+
179
192
  yield* ensureOpen(operation);
180
193
  const request = yield* decodeBoundary(MemoryIndexReplacement.Wire, rawRequest, operation);
181
194
  const source = freezeSource(request.source);
182
195
  const sourceBytes = sourceIdentityBytes(source);
183
196
  const chunks = Object.freeze(request.chunks.map(freezeChunk));
197
+
184
198
  if (source.source.id !== source.key.id) return yield* error(operation, "invalid-input");
185
199
  if (!sameProfile(request.profile, profile)) return yield* error(operation, "incompatible");
186
200
  yield* validateChunks(chunks, profile, operation);
187
201
  const indexedAt = yield* Clock.currentTimeMillis;
202
+
188
203
  const failure = yield* Ref.modify(
189
204
  data,
190
205
  (current): readonly [MemoryIndexError | undefined, IndexData] => {
191
206
  if (current.closed) return [error(operation, "unavailable"), current];
192
207
  const id = keyString(source.key);
193
208
  const existing = current.entries.get(id);
209
+
194
210
  if (
195
211
  existing !== undefined &&
196
212
  (existing._tag === "Withdrawn" || sourceIsFenced(source, existing))
@@ -201,17 +217,22 @@ const makeIndex = Effect.fn("InMemorySemanticIndex.make")(function* (
201
217
  return [error(operation, "budget"), current];
202
218
  }
203
219
  const nextSourceBytes = current.sourceBytes - (existing?.sourceBytes ?? 0) + sourceBytes;
220
+
204
221
  if (nextSourceBytes > maxSourceBytes) return [error(operation, "budget"), current];
205
222
  let count = chunks.length;
223
+
206
224
  for (const [entryId, entry] of current.entries) {
207
225
  if (entryId !== id && entry._tag === "Indexed") count += entry.chunks.length;
208
226
  }
209
227
  if (count > capacity.maxChunks) return [error(operation, "budget"), current];
210
228
  const entries = new Map(current.entries);
229
+
211
230
  entries.set(id, { _tag: "Indexed", source, sourceBytes, chunks, indexedAt });
231
+
212
232
  return [undefined, { ...current, entries, sourceBytes: nextSourceBytes }];
213
233
  },
214
234
  );
235
+
215
236
  if (failure !== undefined) return yield* failure;
216
237
  });
217
238
 
@@ -219,18 +240,24 @@ const makeIndex = Effect.fn("InMemorySemanticIndex.make")(function* (
219
240
  "InMemorySemanticIndex.withdraw",
220
241
  )(function* (rawSource) {
221
242
  const operation = "withdraw semantic memory source";
243
+
222
244
  yield* ensureOpen(operation);
245
+
223
246
  const source = freezeSource(
224
247
  yield* decodeBoundary(MemoryIndexSource.Wire, rawSource, operation),
225
248
  );
249
+
226
250
  const sourceBytes = sourceIdentityBytes(source);
251
+
227
252
  if (source.source.id !== source.key.id) return yield* error(operation, "invalid-input");
253
+
228
254
  const failure = yield* Ref.modify(
229
255
  data,
230
256
  (current): readonly [MemoryIndexError | undefined, IndexData] => {
231
257
  if (current.closed) return [error(operation, "unavailable"), current];
232
258
  const id = keyString(source.key);
233
259
  const existing = current.entries.get(id);
260
+
234
261
  if (existing !== undefined) {
235
262
  if (existing._tag === "Withdrawn") {
236
263
  return [
@@ -243,26 +270,34 @@ const makeIndex = Effect.fn("InMemorySemanticIndex.make")(function* (
243
270
  return [error(operation, "budget"), current];
244
271
  }
245
272
  const nextSourceBytes = current.sourceBytes - (existing?.sourceBytes ?? 0) + sourceBytes;
273
+
246
274
  if (nextSourceBytes > maxSourceBytes) return [error(operation, "budget"), current];
247
275
  const entries = new Map(current.entries);
276
+
248
277
  entries.set(id, { _tag: "Withdrawn", source, sourceBytes });
278
+
249
279
  return [undefined, { ...current, entries, sourceBytes: nextSourceBytes }];
250
280
  },
251
281
  );
282
+
252
283
  if (failure !== undefined) return yield* failure;
253
284
  });
254
285
 
255
286
  const search = Effect.fn("InMemorySemanticIndex.search")(function* (rawQuery: MemoryIndexQuery) {
256
287
  const operation = "search semantic memory index";
288
+
257
289
  yield* ensureOpen(operation);
258
290
  const query = yield* decodeBoundary(MemoryIndexQuery.Wire, rawQuery, operation);
259
291
  const vector = Object.freeze([...query.vector]);
292
+
260
293
  if (!validVector(vector, profile)) return yield* error(operation, "invalid-input");
261
294
  const current = yield* Ref.get(data);
295
+
262
296
  if (current.closed) return yield* error(operation, "unavailable");
263
297
  let scannedChunks = 0;
264
298
  let inspectedSources = 0;
265
299
  const candidates: Array<MemoryIndexCandidate> = [];
300
+
266
301
  for (const entry of current.entries.values()) {
267
302
  inspectedSources += 1;
268
303
  if (inspectedSources % 128 === 0) yield* Effect.yieldNow;
@@ -283,6 +318,7 @@ const makeIndex = Effect.fn("InMemorySemanticIndex.make")(function* (
283
318
  yield* Effect.yieldNow;
284
319
  for (const chunk of entry.chunks) {
285
320
  const score = cosine(vector, chunk.vector);
321
+
286
322
  if (score < query.minScore) continue;
287
323
  candidates.push(
288
324
  MemoryIndexCandidate.make({
@@ -306,6 +342,7 @@ const makeIndex = Effect.fn("InMemorySemanticIndex.make")(function* (
306
342
  left.ordinal - right.ordinal,
307
343
  );
308
344
  yield* ensureOpen(operation);
345
+
309
346
  return MemoryIndexSearch.make({ candidates: candidates.slice(0, query.limit), scannedChunks });
310
347
  });
311
348
 
package/src/testing.ts CHANGED
@@ -9,6 +9,7 @@ export {
9
9
  type SubmissionLedgerConformanceCase,
10
10
  type SubmissionLedgerConformanceFailure,
11
11
  } from "@effect-agent/thread/testing";
12
+
12
13
  export {
13
14
  scheduleStoreConformanceCases,
14
15
  ScheduleStoreConformanceViolation,