@pyxmate/memory 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-DZZHJ66P.mjs → chunk-K7JZXUBN.mjs} +1 -1
- package/dist/chunk-W4LB326D.mjs +618 -0
- package/dist/dashboard.mjs +2 -2
- package/dist/index.d.ts +158 -33
- package/dist/index.mjs +1 -1
- package/dist/react.mjs +2 -2
- package/package.json +1 -1
- package/skills/pyx-memory/patterns/access-control.md +1 -1
- package/skills/pyx-memory/patterns/consumer.md +10 -4
- package/skills/pyx-memory/patterns/file-uploads.md +1 -1
- package/skills/pyx-memory/reference/http-api.md +46 -41
- package/skills/pyx-memory/reference/sdk-guide.md +23 -15
- package/skills/pyx-memory/reference/types.md +5 -7
- package/dist/chunk-4YIKI2BA.mjs +0 -404
package/dist/chunk-4YIKI2BA.mjs
DELETED
|
@@ -1,404 +0,0 @@
|
|
|
1
|
-
// ../client/src/memory-client.ts
|
|
2
|
-
var MemoryServerError = class extends Error {
|
|
3
|
-
status;
|
|
4
|
-
constructor(message, status) {
|
|
5
|
-
super(message);
|
|
6
|
-
this.name = "MemoryServerError";
|
|
7
|
-
this.status = status;
|
|
8
|
-
}
|
|
9
|
-
/** True when the server returned HTTP 404 (not found). */
|
|
10
|
-
get isNotFound() {
|
|
11
|
-
return this.status === 404;
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
|
-
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
15
|
-
var MemoryClient = class {
|
|
16
|
-
baseUrl;
|
|
17
|
-
_authHeaders;
|
|
18
|
-
_requestTimeoutMs;
|
|
19
|
-
constructor(memoryUrl, apiKeyOrOptions) {
|
|
20
|
-
this.baseUrl = memoryUrl.replace(/\/$/, "");
|
|
21
|
-
let apiKey;
|
|
22
|
-
let defaultHeaders = {};
|
|
23
|
-
let requestTimeoutMs = DEFAULT_REQUEST_TIMEOUT_MS;
|
|
24
|
-
if (typeof apiKeyOrOptions === "string") {
|
|
25
|
-
apiKey = apiKeyOrOptions;
|
|
26
|
-
} else if (apiKeyOrOptions) {
|
|
27
|
-
apiKey = apiKeyOrOptions.apiKey;
|
|
28
|
-
defaultHeaders = apiKeyOrOptions.defaultHeaders ?? {};
|
|
29
|
-
if (apiKeyOrOptions.requestTimeoutMs !== void 0) {
|
|
30
|
-
requestTimeoutMs = apiKeyOrOptions.requestTimeoutMs;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
const trimmed = apiKey?.trim();
|
|
34
|
-
this._authHeaders = {
|
|
35
|
-
...trimmed ? { Authorization: `Bearer ${trimmed}` } : {},
|
|
36
|
-
...defaultHeaders
|
|
37
|
-
};
|
|
38
|
-
this._requestTimeoutMs = requestTimeoutMs;
|
|
39
|
-
}
|
|
40
|
-
/** Encode a path segment to prevent URL injection */
|
|
41
|
-
encodePathSegment(segment) {
|
|
42
|
-
return encodeURIComponent(segment);
|
|
43
|
-
}
|
|
44
|
-
async initialize() {
|
|
45
|
-
const response = await fetch(`${this.baseUrl}/health`, {
|
|
46
|
-
headers: this._authHeaders
|
|
47
|
-
});
|
|
48
|
-
if (!response.ok) {
|
|
49
|
-
throw new Error(`Memory server not reachable at ${this.baseUrl}: ${response.status}`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
async store(entry) {
|
|
53
|
-
return this.fetchApi("/api/memory/ingest", {
|
|
54
|
-
method: "POST",
|
|
55
|
-
body: JSON.stringify(entry)
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
async search(params) {
|
|
59
|
-
const searchParams = new URLSearchParams({ query: params.query });
|
|
60
|
-
if (params.limit) searchParams.set("limit", String(params.limit));
|
|
61
|
-
if (params.type) searchParams.set("type", params.type);
|
|
62
|
-
if (params.agentId) searchParams.set("agentId", params.agentId);
|
|
63
|
-
if (params.strategy) searchParams.set("strategy", params.strategy);
|
|
64
|
-
if (params.eventTimeRange) {
|
|
65
|
-
searchParams.set("eventTimeStart", params.eventTimeRange[0]);
|
|
66
|
-
searchParams.set("eventTimeEnd", params.eventTimeRange[1]);
|
|
67
|
-
}
|
|
68
|
-
if (params.asOf) searchParams.set("asOf", params.asOf);
|
|
69
|
-
if (params.abstentionThreshold != null)
|
|
70
|
-
searchParams.set("abstentionThreshold", String(params.abstentionThreshold));
|
|
71
|
-
return this.fetchApi(`/api/memory/search?${searchParams}`);
|
|
72
|
-
}
|
|
73
|
-
async get(id) {
|
|
74
|
-
try {
|
|
75
|
-
return await this.fetchApi(`/api/memory/entries/${this.encodePathSegment(id)}`);
|
|
76
|
-
} catch (error) {
|
|
77
|
-
if (error instanceof MemoryServerError && error.isNotFound) return null;
|
|
78
|
-
throw error;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
async delete(id) {
|
|
82
|
-
try {
|
|
83
|
-
await this.fetchApi(`/api/memory/entries/${this.encodePathSegment(id)}`, {
|
|
84
|
-
method: "DELETE"
|
|
85
|
-
});
|
|
86
|
-
return true;
|
|
87
|
-
} catch (error) {
|
|
88
|
-
if (error instanceof MemoryServerError && error.isNotFound) return false;
|
|
89
|
-
throw error;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
async clearSession(sessionId) {
|
|
93
|
-
const result = await this.fetchApi(
|
|
94
|
-
`/api/memory/sessions/${this.encodePathSegment(sessionId)}`,
|
|
95
|
-
{ method: "DELETE" }
|
|
96
|
-
);
|
|
97
|
-
return result.cleared;
|
|
98
|
-
}
|
|
99
|
-
async stats() {
|
|
100
|
-
const stats = await this.fetchApi("/api/memory/stats");
|
|
101
|
-
return { ...stats, connected: true };
|
|
102
|
-
}
|
|
103
|
-
async shutdown() {
|
|
104
|
-
}
|
|
105
|
-
async list(params = {}) {
|
|
106
|
-
const searchParams = new URLSearchParams();
|
|
107
|
-
if (params.page != null) searchParams.set("page", String(params.page));
|
|
108
|
-
if (params.limit != null) searchParams.set("limit", String(params.limit));
|
|
109
|
-
if (params.type) searchParams.set("type", params.type);
|
|
110
|
-
if (params.agentId) searchParams.set("agentId", params.agentId);
|
|
111
|
-
const qs = searchParams.toString();
|
|
112
|
-
return this.fetchApi(`/api/memory/entries${qs ? `?${qs}` : ""}`);
|
|
113
|
-
}
|
|
114
|
-
// --- Additional endpoints ---
|
|
115
|
-
/**
|
|
116
|
-
* Ingest a file with optional two-phase enrichment.
|
|
117
|
-
*
|
|
118
|
-
* Without enrichment callbacks: standard single-phase ingest (backwards compatible).
|
|
119
|
-
* With enrichment callbacks: fetches extracted images, calls describeImage for each,
|
|
120
|
-
* optionally extracts entities, then submits enrichment data back to the server.
|
|
121
|
-
*/
|
|
122
|
-
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: two-phase enrichment fans out across image-describe + entity-extract + v1/v2 negotiation; each branch maps to a documented case in memory-client-enrichment.test.ts
|
|
123
|
-
async ingestFile(file, options) {
|
|
124
|
-
const formData = new FormData();
|
|
125
|
-
formData.append("file", file);
|
|
126
|
-
if (options?.description) {
|
|
127
|
-
formData.append("description", options.description);
|
|
128
|
-
}
|
|
129
|
-
const wantsTextWindows = Boolean(
|
|
130
|
-
options?.enrichment?.extractEntities || options?.enrichment?.extractEntitiesV2
|
|
131
|
-
);
|
|
132
|
-
const headers = { ...this._authHeaders };
|
|
133
|
-
if (wantsTextWindows) {
|
|
134
|
-
headers["X-Pyx-Enrichment-Capabilities"] = "text_windows_v1";
|
|
135
|
-
}
|
|
136
|
-
const res = await fetch(`${this.baseUrl}/api/memory/ingest/file`, {
|
|
137
|
-
method: "POST",
|
|
138
|
-
body: formData,
|
|
139
|
-
headers
|
|
140
|
-
});
|
|
141
|
-
const result = await this.parseApiResponse(res);
|
|
142
|
-
if (!result.enrichment || !options?.enrichment) {
|
|
143
|
-
return result;
|
|
144
|
-
}
|
|
145
|
-
const enrichment = result.enrichment;
|
|
146
|
-
const { fileId, token, expiresAt, images } = enrichment;
|
|
147
|
-
const isV2 = "version" in enrichment;
|
|
148
|
-
const textWindows = isV2 ? enrichment.textWindows : [];
|
|
149
|
-
const descriptions = [];
|
|
150
|
-
const describeImage = options.enrichment.describeImage;
|
|
151
|
-
if (describeImage && images.length > 0) {
|
|
152
|
-
const CONCURRENCY = 5;
|
|
153
|
-
for (let i = 0; i < images.length; i += CONCURRENCY) {
|
|
154
|
-
const batch = images.slice(i, i + CONCURRENCY);
|
|
155
|
-
const batchResults = await Promise.all(
|
|
156
|
-
batch.map(async (imageMeta) => {
|
|
157
|
-
const imageRes = await fetch(
|
|
158
|
-
`${this.baseUrl}/api/memory/files/${fileId}/images/${imageMeta.imageId}?token=${encodeURIComponent(token)}`,
|
|
159
|
-
{ headers: this._authHeaders }
|
|
160
|
-
);
|
|
161
|
-
if (!imageRes.ok) {
|
|
162
|
-
throw new MemoryServerError(
|
|
163
|
-
`Failed to fetch image ${imageMeta.imageId}: ${imageRes.status}`,
|
|
164
|
-
imageRes.status
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
const imageBuffer = await imageRes.arrayBuffer();
|
|
168
|
-
const description = await describeImage(imageBuffer, imageMeta);
|
|
169
|
-
return { imageId: imageMeta.imageId, description };
|
|
170
|
-
})
|
|
171
|
-
);
|
|
172
|
-
descriptions.push(...batchResults);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
let entities;
|
|
176
|
-
let relationships;
|
|
177
|
-
const v2Callback = options.enrichment.extractEntitiesV2;
|
|
178
|
-
const legacyCallback = options.enrichment.extractEntities;
|
|
179
|
-
const imageDescriptionTexts = descriptions.map((d) => d.description);
|
|
180
|
-
if (v2Callback && (textWindows.length > 0 || imageDescriptionTexts.length > 0)) {
|
|
181
|
-
const extracted = await v2Callback({
|
|
182
|
-
textWindows,
|
|
183
|
-
imageDescriptions: imageDescriptionTexts,
|
|
184
|
-
mimeType: file.type,
|
|
185
|
-
filename: file.name
|
|
186
|
-
});
|
|
187
|
-
entities = extracted.entities;
|
|
188
|
-
relationships = extracted.relationships;
|
|
189
|
-
} else if (legacyCallback) {
|
|
190
|
-
const allInputs = [...textWindows, ...imageDescriptionTexts];
|
|
191
|
-
if (allInputs.length > 0) {
|
|
192
|
-
const extracted = await legacyCallback(allInputs);
|
|
193
|
-
entities = extracted.entities;
|
|
194
|
-
relationships = extracted.relationships;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
const hasGraph = (entities?.length ?? 0) > 0;
|
|
198
|
-
const hasImages = descriptions.length > 0;
|
|
199
|
-
if (!hasGraph && !hasImages) {
|
|
200
|
-
return result;
|
|
201
|
-
}
|
|
202
|
-
const enrichTokenHeader = `${token}:${expiresAt}`;
|
|
203
|
-
const enrichRes = await fetch(`${this.baseUrl}/api/memory/files/${fileId}/enrich`, {
|
|
204
|
-
method: "POST",
|
|
205
|
-
headers: {
|
|
206
|
-
"Content-Type": "application/json",
|
|
207
|
-
"X-Enrichment-Token": enrichTokenHeader,
|
|
208
|
-
...this._authHeaders
|
|
209
|
-
},
|
|
210
|
-
body: JSON.stringify({
|
|
211
|
-
imageDescriptions: descriptions,
|
|
212
|
-
entities,
|
|
213
|
-
relationships
|
|
214
|
-
})
|
|
215
|
-
});
|
|
216
|
-
if (!enrichRes.ok) {
|
|
217
|
-
const body = await enrichRes.json().catch(() => ({}));
|
|
218
|
-
throw new MemoryServerError(
|
|
219
|
-
body.error ?? `Enrichment failed: ${enrichRes.status}`,
|
|
220
|
-
enrichRes.status
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
const enrichData = await this.parseApiResponse(enrichRes);
|
|
224
|
-
const enrichCharacters = descriptions.reduce((sum, d) => sum + d.description.length, 0);
|
|
225
|
-
result.entryIds.push(...enrichData.entryIds);
|
|
226
|
-
result.chunks += descriptions.length;
|
|
227
|
-
result.totalCharacters += enrichCharacters;
|
|
228
|
-
return result;
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Get the download URL for an uploaded file.
|
|
232
|
-
* Returns a URL that serves the original file binary with proper Content-Type.
|
|
233
|
-
*/
|
|
234
|
-
getFileDownloadUrl(filename) {
|
|
235
|
-
return `${this.baseUrl}/api/memory/files/download/${encodeURIComponent(filename)}`;
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* Download an uploaded file by filename.
|
|
239
|
-
* Returns the raw Response (caller handles the body — arrayBuffer, blob, stream, etc.).
|
|
240
|
-
*/
|
|
241
|
-
async downloadFile(filename) {
|
|
242
|
-
const url = this.getFileDownloadUrl(filename);
|
|
243
|
-
const res = await fetch(url, { headers: this._authHeaders });
|
|
244
|
-
if (!res.ok) {
|
|
245
|
-
throw new MemoryServerError(`File download failed: ${res.status}`, res.status);
|
|
246
|
-
}
|
|
247
|
-
return res;
|
|
248
|
-
}
|
|
249
|
-
/** @deprecated Use {@link list} instead. Kept for backwards compatibility. */
|
|
250
|
-
async listEntries(params) {
|
|
251
|
-
const result = await this.list(params);
|
|
252
|
-
return result.entries;
|
|
253
|
-
}
|
|
254
|
-
async graphNodes() {
|
|
255
|
-
const result = await this.fetchApi(
|
|
256
|
-
"/api/memory/graph/nodes"
|
|
257
|
-
);
|
|
258
|
-
return result.nodes;
|
|
259
|
-
}
|
|
260
|
-
async graphEdges() {
|
|
261
|
-
return this.fetchApi(
|
|
262
|
-
"/api/memory/graph/edges"
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
async graphQuery(query) {
|
|
266
|
-
return this.fetchApi("/api/memory/graph/query", {
|
|
267
|
-
method: "POST",
|
|
268
|
-
body: JSON.stringify(query)
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
// --- ExtendedMemoryInterface methods ---
|
|
272
|
-
async consolidate() {
|
|
273
|
-
return this.fetchApi("/api/memory/consolidate", {
|
|
274
|
-
method: "POST"
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
async forget(id, reason) {
|
|
278
|
-
try {
|
|
279
|
-
await this.fetchApi(`/api/memory/forget/${this.encodePathSegment(id)}`, {
|
|
280
|
-
method: "POST",
|
|
281
|
-
body: JSON.stringify({ reason })
|
|
282
|
-
});
|
|
283
|
-
return true;
|
|
284
|
-
} catch (error) {
|
|
285
|
-
if (error instanceof MemoryServerError && error.isNotFound) return false;
|
|
286
|
-
throw error;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
async summarizeSession(sessionId) {
|
|
290
|
-
try {
|
|
291
|
-
return await this.fetchApi(
|
|
292
|
-
`/api/memory/sessions/${this.encodePathSegment(sessionId)}/summarize`,
|
|
293
|
-
{ method: "POST" }
|
|
294
|
-
);
|
|
295
|
-
} catch (error) {
|
|
296
|
-
if (error instanceof MemoryServerError && error.isNotFound) return null;
|
|
297
|
-
throw error;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
async runDecay() {
|
|
301
|
-
const result = await this.fetchApi("/api/memory/decay", {
|
|
302
|
-
method: "POST"
|
|
303
|
-
});
|
|
304
|
-
return result.archived;
|
|
305
|
-
}
|
|
306
|
-
async reindex() {
|
|
307
|
-
await this.fetchApi("/api/memory/reindex", { method: "POST" });
|
|
308
|
-
}
|
|
309
|
-
async clearGraph() {
|
|
310
|
-
const result = await this.fetchApi("/api/memory/graph/clear", {
|
|
311
|
-
method: "POST"
|
|
312
|
-
});
|
|
313
|
-
return result.deleted;
|
|
314
|
-
}
|
|
315
|
-
async deleteBySource(source) {
|
|
316
|
-
const result = await this.fetchApi(
|
|
317
|
-
`/api/memory/source/${this.encodePathSegment(source)}`,
|
|
318
|
-
{ method: "DELETE" }
|
|
319
|
-
);
|
|
320
|
-
return result.deleted;
|
|
321
|
-
}
|
|
322
|
-
async queryAsOf(asOfDate, filters = {}) {
|
|
323
|
-
const params = new URLSearchParams({ asOf: asOfDate });
|
|
324
|
-
if (filters.type) params.set("type", filters.type);
|
|
325
|
-
if (filters.agentId) params.set("agentId", filters.agentId);
|
|
326
|
-
if (filters.source) params.set("source", filters.source);
|
|
327
|
-
if (filters.limit) params.set("limit", String(filters.limit));
|
|
328
|
-
const result = await this.fetchApi(
|
|
329
|
-
`/api/memory/query-as-of?${params}`
|
|
330
|
-
);
|
|
331
|
-
return result.entries;
|
|
332
|
-
}
|
|
333
|
-
async queryByEventTime(startTime, endTime, filters = {}) {
|
|
334
|
-
const params = new URLSearchParams({ startTime, endTime });
|
|
335
|
-
if (filters.type) params.set("type", filters.type);
|
|
336
|
-
if (filters.agentId) params.set("agentId", filters.agentId);
|
|
337
|
-
if (filters.source) params.set("source", filters.source);
|
|
338
|
-
if (filters.limit) params.set("limit", String(filters.limit));
|
|
339
|
-
const result = await this.fetchApi(
|
|
340
|
-
`/api/memory/query-by-event-time?${params}`
|
|
341
|
-
);
|
|
342
|
-
return result.entries;
|
|
343
|
-
}
|
|
344
|
-
async fetchApi(path, options) {
|
|
345
|
-
const signal = options?.signal ?? AbortSignal.timeout(this._requestTimeoutMs);
|
|
346
|
-
let res;
|
|
347
|
-
try {
|
|
348
|
-
res = await fetch(`${this.baseUrl}${path}`, {
|
|
349
|
-
...options,
|
|
350
|
-
headers: {
|
|
351
|
-
"Content-Type": "application/json",
|
|
352
|
-
...options?.headers,
|
|
353
|
-
...this._authHeaders
|
|
354
|
-
},
|
|
355
|
-
signal
|
|
356
|
-
});
|
|
357
|
-
} catch (err) {
|
|
358
|
-
throw this.translateFetchError(err, path);
|
|
359
|
-
}
|
|
360
|
-
return this.parseApiResponse(res);
|
|
361
|
-
}
|
|
362
|
-
/**
|
|
363
|
-
* Map fetch-layer rejections into a typed `MemoryServerError` so callers
|
|
364
|
-
* can react uniformly. AbortSignal.timeout fires a `TimeoutError`; the
|
|
365
|
-
* caller's signal generally fires an `AbortError`. Anything else (DNS,
|
|
366
|
-
* TCP reset, TLS) becomes a wrapped error with status 0.
|
|
367
|
-
*/
|
|
368
|
-
translateFetchError(err, path) {
|
|
369
|
-
if (err instanceof Error) {
|
|
370
|
-
if (err.name === "TimeoutError") {
|
|
371
|
-
return new MemoryServerError(
|
|
372
|
-
`Memory server request timed out after ${this._requestTimeoutMs}ms (${path})`,
|
|
373
|
-
504
|
|
374
|
-
);
|
|
375
|
-
}
|
|
376
|
-
if (err.name === "AbortError") {
|
|
377
|
-
return new MemoryServerError(`Memory server request aborted (${path})`, 499);
|
|
378
|
-
}
|
|
379
|
-
return new MemoryServerError(`Memory server request failed: ${err.message} (${path})`, 0);
|
|
380
|
-
}
|
|
381
|
-
return new MemoryServerError(`Memory server request failed: ${String(err)} (${path})`, 0);
|
|
382
|
-
}
|
|
383
|
-
/** Parse and validate a JSON API response, throwing MemoryServerError on any failure. */
|
|
384
|
-
async parseApiResponse(res) {
|
|
385
|
-
let body;
|
|
386
|
-
try {
|
|
387
|
-
body = await res.json();
|
|
388
|
-
} catch {
|
|
389
|
-
throw new MemoryServerError(
|
|
390
|
-
`Memory server error: invalid JSON response (${res.status})`,
|
|
391
|
-
res.status
|
|
392
|
-
);
|
|
393
|
-
}
|
|
394
|
-
if (!body?.success || body.data == null) {
|
|
395
|
-
throw new MemoryServerError(body?.error ?? `Memory server error: ${res.status}`, res.status);
|
|
396
|
-
}
|
|
397
|
-
return body.data;
|
|
398
|
-
}
|
|
399
|
-
};
|
|
400
|
-
|
|
401
|
-
export {
|
|
402
|
-
MemoryServerError,
|
|
403
|
-
MemoryClient
|
|
404
|
-
};
|