@memorylayerai/sdk 0.1.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/index.js ADDED
@@ -0,0 +1,709 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __esm = (fn, res) => function __init() {
6
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
7
+ };
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+
22
+ // src/errors.ts
23
+ var MemoryLayerError, AuthenticationError, RateLimitError, ValidationError, NetworkError, APIError;
24
+ var init_errors = __esm({
25
+ "src/errors.ts"() {
26
+ "use strict";
27
+ MemoryLayerError = class _MemoryLayerError extends Error {
28
+ constructor(message, statusCode, requestId, details) {
29
+ super(message);
30
+ this.statusCode = statusCode;
31
+ this.requestId = requestId;
32
+ this.details = details;
33
+ this.name = "MemoryLayerError";
34
+ Object.setPrototypeOf(this, _MemoryLayerError.prototype);
35
+ }
36
+ };
37
+ AuthenticationError = class _AuthenticationError extends MemoryLayerError {
38
+ constructor(message, requestId) {
39
+ super(message, 401, requestId);
40
+ this.name = "AuthenticationError";
41
+ Object.setPrototypeOf(this, _AuthenticationError.prototype);
42
+ }
43
+ };
44
+ RateLimitError = class _RateLimitError extends MemoryLayerError {
45
+ constructor(message, retryAfter, requestId) {
46
+ super(message, 429, requestId);
47
+ this.retryAfter = retryAfter;
48
+ this.name = "RateLimitError";
49
+ Object.setPrototypeOf(this, _RateLimitError.prototype);
50
+ }
51
+ };
52
+ ValidationError = class _ValidationError extends MemoryLayerError {
53
+ constructor(message, errors, requestId) {
54
+ super(message, 400, requestId);
55
+ this.errors = errors;
56
+ this.name = "ValidationError";
57
+ Object.setPrototypeOf(this, _ValidationError.prototype);
58
+ }
59
+ };
60
+ NetworkError = class _NetworkError extends MemoryLayerError {
61
+ constructor(message, cause) {
62
+ super(message);
63
+ this.cause = cause;
64
+ this.name = "NetworkError";
65
+ Object.setPrototypeOf(this, _NetworkError.prototype);
66
+ }
67
+ };
68
+ APIError = class _APIError extends MemoryLayerError {
69
+ constructor(message, statusCode, requestId, details) {
70
+ super(message, statusCode, requestId, details);
71
+ this.name = "APIError";
72
+ Object.setPrototypeOf(this, _APIError.prototype);
73
+ }
74
+ };
75
+ }
76
+ });
77
+
78
+ // src/resources/memories.ts
79
+ var memories_exports = {};
80
+ __export(memories_exports, {
81
+ MemoriesResource: () => MemoriesResource
82
+ });
83
+ var MemoriesResource;
84
+ var init_memories = __esm({
85
+ "src/resources/memories.ts"() {
86
+ "use strict";
87
+ init_errors();
88
+ MemoriesResource = class {
89
+ constructor(httpClient) {
90
+ this.httpClient = httpClient;
91
+ }
92
+ /**
93
+ * Add a new memory
94
+ * @param request - Memory creation request
95
+ * @returns The created memory
96
+ */
97
+ async add(request) {
98
+ if (!request.content || request.content.trim().length === 0) {
99
+ throw new ValidationError(
100
+ "Memory content cannot be empty",
101
+ [{ field: "content", message: "Content is required and cannot be empty" }]
102
+ );
103
+ }
104
+ if (!request.projectId || request.projectId.trim().length === 0) {
105
+ throw new ValidationError(
106
+ "Project ID is required",
107
+ [{ field: "projectId", message: "Project ID is required" }]
108
+ );
109
+ }
110
+ return this.httpClient.request({
111
+ method: "POST",
112
+ path: "/v1/memories",
113
+ body: request
114
+ });
115
+ }
116
+ /**
117
+ * Get a memory by ID
118
+ * @param id - Memory ID
119
+ * @returns The memory
120
+ */
121
+ async get(id) {
122
+ if (!id || id.trim().length === 0) {
123
+ throw new ValidationError(
124
+ "Memory ID is required",
125
+ [{ field: "id", message: "Memory ID is required" }]
126
+ );
127
+ }
128
+ return this.httpClient.request({
129
+ method: "GET",
130
+ path: `/v1/memories/${id}`
131
+ });
132
+ }
133
+ /**
134
+ * Update a memory
135
+ * @param id - Memory ID
136
+ * @param request - Update request
137
+ * @returns The updated memory
138
+ */
139
+ async update(id, request) {
140
+ if (!id || id.trim().length === 0) {
141
+ throw new ValidationError(
142
+ "Memory ID is required",
143
+ [{ field: "id", message: "Memory ID is required" }]
144
+ );
145
+ }
146
+ if (!request.content && !request.metadata) {
147
+ throw new ValidationError(
148
+ "At least one of content or metadata must be provided",
149
+ [{ field: "request", message: "Nothing to update" }]
150
+ );
151
+ }
152
+ return this.httpClient.request({
153
+ method: "PATCH",
154
+ path: `/v1/memories/${id}`,
155
+ body: request
156
+ });
157
+ }
158
+ /**
159
+ * Delete a memory
160
+ * @param id - Memory ID
161
+ */
162
+ async delete(id) {
163
+ if (!id || id.trim().length === 0) {
164
+ throw new ValidationError(
165
+ "Memory ID is required",
166
+ [{ field: "id", message: "Memory ID is required" }]
167
+ );
168
+ }
169
+ await this.httpClient.request({
170
+ method: "DELETE",
171
+ path: `/v1/memories/${id}`
172
+ });
173
+ }
174
+ /**
175
+ * List memories
176
+ * @param request - List request with filters
177
+ * @returns Array of memories
178
+ */
179
+ async list(request) {
180
+ if (!request.projectId || request.projectId.trim().length === 0) {
181
+ throw new ValidationError(
182
+ "Project ID is required",
183
+ [{ field: "projectId", message: "Project ID is required" }]
184
+ );
185
+ }
186
+ const query = {
187
+ projectId: request.projectId
188
+ };
189
+ if (request.limit !== void 0) {
190
+ query.limit = request.limit.toString();
191
+ }
192
+ if (request.offset !== void 0) {
193
+ query.offset = request.offset.toString();
194
+ }
195
+ if (request.filter) {
196
+ query.filter = JSON.stringify(request.filter);
197
+ }
198
+ return this.httpClient.request({
199
+ method: "GET",
200
+ path: "/v1/memories",
201
+ query
202
+ });
203
+ }
204
+ };
205
+ }
206
+ });
207
+
208
+ // src/resources/search.ts
209
+ var search_exports = {};
210
+ __export(search_exports, {
211
+ SearchResource: () => SearchResource
212
+ });
213
+ var SearchResource;
214
+ var init_search = __esm({
215
+ "src/resources/search.ts"() {
216
+ "use strict";
217
+ init_errors();
218
+ SearchResource = class {
219
+ constructor(httpClient) {
220
+ this.httpClient = httpClient;
221
+ }
222
+ /**
223
+ * Search memories
224
+ * @param request - Search request
225
+ * @returns Search results
226
+ */
227
+ async search(request) {
228
+ if (!request.query || request.query.trim().length === 0) {
229
+ throw new ValidationError(
230
+ "Search query cannot be empty",
231
+ [{ field: "query", message: "Query is required and cannot be empty" }]
232
+ );
233
+ }
234
+ if (!request.projectId || request.projectId.trim().length === 0) {
235
+ throw new ValidationError(
236
+ "Project ID is required",
237
+ [{ field: "projectId", message: "Project ID is required" }]
238
+ );
239
+ }
240
+ const query = {
241
+ q: request.query,
242
+ projectId: request.projectId
243
+ };
244
+ if (request.limit !== void 0) {
245
+ query.limit = request.limit.toString();
246
+ }
247
+ if (request.threshold !== void 0) {
248
+ query.threshold = request.threshold.toString();
249
+ }
250
+ if (request.filter) {
251
+ query.filter = JSON.stringify(request.filter);
252
+ }
253
+ return this.httpClient.request({
254
+ method: "GET",
255
+ path: "/v1/search",
256
+ query
257
+ });
258
+ }
259
+ };
260
+ }
261
+ });
262
+
263
+ // src/resources/ingest.ts
264
+ var ingest_exports = {};
265
+ __export(ingest_exports, {
266
+ IngestResource: () => IngestResource
267
+ });
268
+ var IngestResource;
269
+ var init_ingest = __esm({
270
+ "src/resources/ingest.ts"() {
271
+ "use strict";
272
+ init_errors();
273
+ IngestResource = class {
274
+ constructor(httpClient) {
275
+ this.httpClient = httpClient;
276
+ }
277
+ /**
278
+ * Ingest a file
279
+ * @param request - File ingestion request
280
+ * @returns Ingestion response with created memory IDs
281
+ */
282
+ async file(request) {
283
+ if (!request.file) {
284
+ throw new ValidationError(
285
+ "File is required",
286
+ [{ field: "file", message: "File is required" }]
287
+ );
288
+ }
289
+ if (!request.projectId || request.projectId.trim().length === 0) {
290
+ throw new ValidationError(
291
+ "Project ID is required",
292
+ [{ field: "projectId", message: "Project ID is required" }]
293
+ );
294
+ }
295
+ const body = {
296
+ projectId: request.projectId,
297
+ metadata: request.metadata,
298
+ chunkSize: request.chunkSize,
299
+ chunkOverlap: request.chunkOverlap,
300
+ // In a real implementation, you'd convert the file to base64 or use FormData
301
+ file: request.file
302
+ };
303
+ return this.httpClient.request({
304
+ method: "POST",
305
+ path: "/v1/ingest/file",
306
+ body
307
+ });
308
+ }
309
+ /**
310
+ * Ingest text
311
+ * @param request - Text ingestion request
312
+ * @returns Ingestion response with created memory IDs
313
+ */
314
+ async text(request) {
315
+ if (!request.text || request.text.trim().length === 0) {
316
+ throw new ValidationError(
317
+ "Text cannot be empty",
318
+ [{ field: "text", message: "Text is required and cannot be empty" }]
319
+ );
320
+ }
321
+ if (!request.projectId || request.projectId.trim().length === 0) {
322
+ throw new ValidationError(
323
+ "Project ID is required",
324
+ [{ field: "projectId", message: "Project ID is required" }]
325
+ );
326
+ }
327
+ return this.httpClient.request({
328
+ method: "POST",
329
+ path: "/v1/ingest/text",
330
+ body: request
331
+ });
332
+ }
333
+ };
334
+ }
335
+ });
336
+
337
+ // src/resources/router.ts
338
+ var router_exports = {};
339
+ __export(router_exports, {
340
+ RouterResource: () => RouterResource
341
+ });
342
+ var RouterResource;
343
+ var init_router = __esm({
344
+ "src/resources/router.ts"() {
345
+ "use strict";
346
+ init_errors();
347
+ RouterResource = class {
348
+ constructor(httpClient) {
349
+ this.httpClient = httpClient;
350
+ }
351
+ /**
352
+ * Complete a router request (non-streaming)
353
+ * @param request - Router request
354
+ * @returns Router response
355
+ */
356
+ async complete(request) {
357
+ if (!request.messages || request.messages.length === 0) {
358
+ throw new ValidationError(
359
+ "Messages array cannot be empty",
360
+ [{ field: "messages", message: "At least one message is required" }]
361
+ );
362
+ }
363
+ if (!request.projectId || request.projectId.trim().length === 0) {
364
+ throw new ValidationError(
365
+ "Project ID is required",
366
+ [{ field: "projectId", message: "Project ID is required" }]
367
+ );
368
+ }
369
+ return this.httpClient.request({
370
+ method: "POST",
371
+ path: "/v1/router/complete",
372
+ body: {
373
+ ...request,
374
+ stream: false
375
+ }
376
+ });
377
+ }
378
+ /**
379
+ * Stream a router request
380
+ * @param request - Router request
381
+ * @returns Async iterable of stream chunks
382
+ */
383
+ async *stream(request) {
384
+ if (!request.messages || request.messages.length === 0) {
385
+ throw new ValidationError(
386
+ "Messages array cannot be empty",
387
+ [{ field: "messages", message: "At least one message is required" }]
388
+ );
389
+ }
390
+ if (!request.projectId || request.projectId.trim().length === 0) {
391
+ throw new ValidationError(
392
+ "Project ID is required",
393
+ [{ field: "projectId", message: "Project ID is required" }]
394
+ );
395
+ }
396
+ const stream = this.httpClient.stream({
397
+ method: "POST",
398
+ path: "/v1/router/complete",
399
+ body: {
400
+ ...request,
401
+ stream: true
402
+ }
403
+ });
404
+ try {
405
+ for await (const chunk of stream) {
406
+ yield chunk;
407
+ }
408
+ } catch (error) {
409
+ throw error;
410
+ }
411
+ }
412
+ };
413
+ }
414
+ });
415
+
416
+ // src/http-client.ts
417
+ init_errors();
418
+ var HTTPClient = class {
419
+ constructor(config, retryConfig) {
420
+ this.config = config;
421
+ this.baseURL = config.baseURL || "https://api.memorylayer.com";
422
+ this.retryConfig = {
423
+ maxRetries: config.maxRetries ?? 3,
424
+ initialDelay: config.retryDelay ?? 1e3,
425
+ maxDelay: 3e4,
426
+ backoffMultiplier: 2,
427
+ retryableStatusCodes: [429, 500, 502, 503, 504],
428
+ ...retryConfig
429
+ };
430
+ }
431
+ /**
432
+ * Make an HTTP request with retry logic
433
+ */
434
+ async request(options) {
435
+ let lastError = null;
436
+ for (let attempt = 0; attempt <= this.retryConfig.maxRetries; attempt++) {
437
+ try {
438
+ return await this.makeRequest(options);
439
+ } catch (error) {
440
+ lastError = error;
441
+ if (!this.shouldRetry(error, attempt)) {
442
+ throw error;
443
+ }
444
+ const delay = this.getRetryDelay(error, attempt);
445
+ this.config.logger?.debug(`Retrying request after ${delay}ms (attempt ${attempt + 1}/${this.retryConfig.maxRetries})`);
446
+ await this.sleep(delay);
447
+ }
448
+ }
449
+ throw lastError;
450
+ }
451
+ /**
452
+ * Make a streaming request
453
+ */
454
+ async *stream(options) {
455
+ const url = this.buildURL(options.path, options.query);
456
+ const headers = this.buildHeaders(options.headers);
457
+ try {
458
+ const response = await fetch(url, {
459
+ method: options.method,
460
+ headers,
461
+ body: options.body ? JSON.stringify(options.body) : void 0,
462
+ signal: AbortSignal.timeout(options.timeout || this.config.timeout || 6e4)
463
+ });
464
+ if (!response.ok) {
465
+ throw await this.handleErrorResponse(response);
466
+ }
467
+ if (!response.body) {
468
+ throw new NetworkError("Response body is null", new Error("No response body"));
469
+ }
470
+ const reader = response.body.getReader();
471
+ const decoder = new TextDecoder();
472
+ let buffer = "";
473
+ try {
474
+ while (true) {
475
+ const { done, value } = await reader.read();
476
+ if (done) {
477
+ break;
478
+ }
479
+ buffer += decoder.decode(value, { stream: true });
480
+ const lines = buffer.split("\n");
481
+ buffer = lines.pop() || "";
482
+ for (const line of lines) {
483
+ if (line.trim() === "") continue;
484
+ if (line.startsWith("data: ")) {
485
+ const data = line.slice(6);
486
+ if (data === "[DONE]") continue;
487
+ try {
488
+ yield JSON.parse(data);
489
+ } catch (e) {
490
+ this.config.logger?.warn("Failed to parse streaming chunk:", data);
491
+ }
492
+ }
493
+ }
494
+ }
495
+ } finally {
496
+ reader.releaseLock();
497
+ }
498
+ } catch (error) {
499
+ if (error instanceof MemoryLayerError) {
500
+ throw error;
501
+ }
502
+ throw new NetworkError("Streaming request failed", error);
503
+ }
504
+ }
505
+ /**
506
+ * Make the actual HTTP request
507
+ */
508
+ async makeRequest(options) {
509
+ const url = this.buildURL(options.path, options.query);
510
+ const headers = this.buildHeaders(options.headers);
511
+ try {
512
+ const response = await fetch(url, {
513
+ method: options.method,
514
+ headers,
515
+ body: options.body ? JSON.stringify(options.body) : void 0,
516
+ signal: AbortSignal.timeout(options.timeout || this.config.timeout || 3e4)
517
+ });
518
+ if (!response.ok) {
519
+ throw await this.handleErrorResponse(response);
520
+ }
521
+ return await response.json();
522
+ } catch (error) {
523
+ if (error instanceof MemoryLayerError) {
524
+ throw error;
525
+ }
526
+ throw new NetworkError("Request failed", error);
527
+ }
528
+ }
529
+ /**
530
+ * Build the full URL with query parameters
531
+ */
532
+ buildURL(path, query) {
533
+ const url = new URL(path, this.baseURL);
534
+ if (query) {
535
+ Object.entries(query).forEach(([key, value]) => {
536
+ url.searchParams.append(key, value);
537
+ });
538
+ }
539
+ return url.toString();
540
+ }
541
+ /**
542
+ * Build request headers
543
+ */
544
+ buildHeaders(additionalHeaders) {
545
+ return {
546
+ "Content-Type": "application/json",
547
+ "Authorization": `Bearer ${this.config.apiKey}`,
548
+ "X-SDK-Version": "0.1.0",
549
+ "X-API-Version": "v1",
550
+ ...this.config.headers,
551
+ ...additionalHeaders
552
+ };
553
+ }
554
+ /**
555
+ * Handle error responses from the API
556
+ */
557
+ async handleErrorResponse(response) {
558
+ const requestId = response.headers.get("x-request-id") || void 0;
559
+ let errorData;
560
+ try {
561
+ errorData = await response.json();
562
+ } catch {
563
+ errorData = { message: response.statusText };
564
+ }
565
+ const message = errorData.error?.message || errorData.message || "Unknown error";
566
+ const details = errorData.error?.details || errorData.details;
567
+ switch (response.status) {
568
+ case 401:
569
+ return new AuthenticationError(message, requestId);
570
+ case 429:
571
+ const retryAfter = parseInt(response.headers.get("retry-after") || "60", 10);
572
+ return new RateLimitError(message, retryAfter, requestId);
573
+ case 400:
574
+ const errors = errorData.error?.errors || [];
575
+ return new ValidationError(message, errors, requestId);
576
+ default:
577
+ return new APIError(message, response.status, requestId, details);
578
+ }
579
+ }
580
+ /**
581
+ * Determine if an error should be retried
582
+ */
583
+ shouldRetry(error, attempt) {
584
+ if (attempt >= this.retryConfig.maxRetries) {
585
+ return false;
586
+ }
587
+ if (error instanceof NetworkError) {
588
+ return true;
589
+ }
590
+ if (error instanceof RateLimitError) {
591
+ return true;
592
+ }
593
+ if (error.statusCode && this.retryConfig.retryableStatusCodes.includes(error.statusCode)) {
594
+ return true;
595
+ }
596
+ if (error.statusCode && error.statusCode >= 400 && error.statusCode < 500) {
597
+ return false;
598
+ }
599
+ return false;
600
+ }
601
+ /**
602
+ * Calculate retry delay with exponential backoff
603
+ */
604
+ getRetryDelay(error, attempt) {
605
+ if (error instanceof RateLimitError) {
606
+ return error.retryAfter * 1e3;
607
+ }
608
+ const exponentialDelay = this.retryConfig.initialDelay * Math.pow(this.retryConfig.backoffMultiplier, attempt);
609
+ const jitter = Math.random() * 0.1 * exponentialDelay;
610
+ const delay = Math.min(exponentialDelay + jitter, this.retryConfig.maxDelay);
611
+ return delay;
612
+ }
613
+ /**
614
+ * Sleep for a specified duration
615
+ */
616
+ sleep(ms) {
617
+ return new Promise((resolve) => setTimeout(resolve, ms));
618
+ }
619
+ };
620
+
621
+ // src/client.ts
622
+ init_errors();
623
+ var MemoryLayerClient = class {
624
+ constructor(config = {}) {
625
+ const apiKey = config.apiKey || process.env.MEMORYLAYER_API_KEY;
626
+ if (!apiKey) {
627
+ throw new ValidationError(
628
+ "API key is required. Provide it via config.apiKey or MEMORYLAYER_API_KEY environment variable.",
629
+ [{ field: "apiKey", message: "API key is required" }]
630
+ );
631
+ }
632
+ if (typeof apiKey !== "string" || apiKey.trim().length === 0) {
633
+ throw new ValidationError(
634
+ "API key must be a non-empty string",
635
+ [{ field: "apiKey", message: "API key must be a non-empty string" }]
636
+ );
637
+ }
638
+ const httpConfig = {
639
+ apiKey,
640
+ baseURL: config.baseURL,
641
+ timeout: config.timeout,
642
+ maxRetries: config.maxRetries,
643
+ retryDelay: config.retryDelay,
644
+ headers: config.headers,
645
+ logger: config.logger
646
+ };
647
+ this.httpClient = new HTTPClient(httpConfig);
648
+ }
649
+ /**
650
+ * Access memory operations
651
+ */
652
+ get memories() {
653
+ if (!this._memories) {
654
+ const { MemoriesResource: MemoriesResource2 } = (init_memories(), __toCommonJS(memories_exports));
655
+ this._memories = new MemoriesResource2(this.httpClient);
656
+ }
657
+ return this._memories;
658
+ }
659
+ /**
660
+ * Access search operations
661
+ */
662
+ get search() {
663
+ if (!this._search) {
664
+ const { SearchResource: SearchResource2 } = (init_search(), __toCommonJS(search_exports));
665
+ this._search = new SearchResource2(this.httpClient);
666
+ }
667
+ return this._search;
668
+ }
669
+ /**
670
+ * Access ingestion operations
671
+ */
672
+ get ingest() {
673
+ if (!this._ingest) {
674
+ const { IngestResource: IngestResource2 } = (init_ingest(), __toCommonJS(ingest_exports));
675
+ this._ingest = new IngestResource2(this.httpClient);
676
+ }
677
+ return this._ingest;
678
+ }
679
+ /**
680
+ * Access router operations
681
+ */
682
+ get router() {
683
+ if (!this._router) {
684
+ const { RouterResource: RouterResource2 } = (init_router(), __toCommonJS(router_exports));
685
+ this._router = new RouterResource2(this.httpClient);
686
+ }
687
+ return this._router;
688
+ }
689
+ };
690
+
691
+ // src/index.ts
692
+ init_errors();
693
+ init_memories();
694
+ init_search();
695
+ init_ingest();
696
+ init_router();
697
+ export {
698
+ APIError,
699
+ AuthenticationError,
700
+ IngestResource,
701
+ MemoriesResource,
702
+ MemoryLayerClient,
703
+ MemoryLayerError,
704
+ NetworkError,
705
+ RateLimitError,
706
+ RouterResource,
707
+ SearchResource,
708
+ ValidationError
709
+ };