@memorylayerai/sdk 0.4.0 → 0.5.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.cjs CHANGED
@@ -221,9 +221,18 @@ var init_search = __esm({
221
221
  this.httpClient = httpClient;
222
222
  }
223
223
  /**
224
- * Search memories
224
+ * Search memories using the unified /v1/search endpoint with hybrid retrieval.
225
+ *
226
+ * This uses the app's full retrieval pipeline with:
227
+ * - Vector similarity search
228
+ * - BM25 keyword search
229
+ * - Recency scoring
230
+ * - Graph connectivity (optional)
231
+ * - Entity expansion (optional)
232
+ * - LLM/Cross-encoder reranking (optional)
233
+ *
225
234
  * @param request - Search request
226
- * @returns Search results
235
+ * @returns Search results with memory pack structure
227
236
  */
228
237
  async search(request) {
229
238
  if (!request.query || request.query.trim().length === 0) {
@@ -238,42 +247,36 @@ var init_search = __esm({
238
247
  [{ field: "projectId", message: "Project ID is required" }]
239
248
  );
240
249
  }
241
- const query = {
242
- q: request.query,
243
- projectId: request.projectId
250
+ const body = {
251
+ query: request.query,
252
+ project_id: request.projectId,
253
+ include_text_format: true
244
254
  };
245
255
  if (request.limit !== void 0) {
246
- query.limit = request.limit.toString();
247
- }
248
- if (request.threshold !== void 0) {
249
- query.threshold = request.threshold.toString();
250
- }
251
- if (request.filter) {
252
- query.filter = JSON.stringify(request.filter);
256
+ body.limit = request.limit;
253
257
  }
254
- if (request.enableQueryRewriting !== void 0) {
255
- query.enableQueryRewriting = request.enableQueryRewriting.toString();
256
- }
257
- if (request.enableEntityExpansion !== void 0) {
258
- query.enableEntityExpansion = request.enableEntityExpansion.toString();
259
- }
260
- if (request.enableGraphConnectivity !== void 0) {
261
- query.enableGraphConnectivity = request.enableGraphConnectivity.toString();
262
- }
263
- if (request.enableSemanticDedup !== void 0) {
264
- query.enableSemanticDedup = request.enableSemanticDedup.toString();
265
- }
266
- if (request.rerankingStrategy) {
267
- query.rerankingStrategy = request.rerankingStrategy;
268
- }
269
- if (request.fusionWeights) {
270
- query.fusionWeights = JSON.stringify(request.fusionWeights);
271
- }
272
- return this.httpClient.request({
273
- method: "GET",
258
+ body.rerank_strategy = request.rerankingStrategy || "cross-encoder";
259
+ const response = await this.httpClient.request({
260
+ method: "POST",
274
261
  path: "/v1/search",
275
- query
262
+ body
276
263
  });
264
+ const memoryPack = response.memory_pack || {};
265
+ const results = [];
266
+ for (const memoryType of ["facts", "preferences", "entities", "sources"]) {
267
+ const items = memoryPack[memoryType] || [];
268
+ for (const item of items) {
269
+ results.push({
270
+ memory: item,
271
+ score: item.score || 1,
272
+ highlights: item.highlights || []
273
+ });
274
+ }
275
+ }
276
+ return {
277
+ results,
278
+ total: results.length
279
+ };
277
280
  }
278
281
  };
279
282
  }
@@ -311,18 +314,15 @@ var init_ingest = __esm({
311
314
  [{ field: "projectId", message: "Project ID is required" }]
312
315
  );
313
316
  }
314
- const body = {
315
- projectId: request.projectId,
316
- metadata: request.metadata,
317
- chunkSize: request.chunkSize,
318
- chunkOverlap: request.chunkOverlap,
319
- // In a real implementation, you'd convert the file to base64 or use FormData
320
- file: request.file
321
- };
322
317
  return this.httpClient.request({
323
318
  method: "POST",
324
- path: "/v1/ingest/file",
325
- body
319
+ path: "/v1/ingest",
320
+ body: {
321
+ type: "pdf",
322
+ projectId: request.projectId,
323
+ metadata: request.metadata || {},
324
+ file: request.file
325
+ }
326
326
  });
327
327
  }
328
328
  /**
@@ -345,9 +345,71 @@ var init_ingest = __esm({
345
345
  }
346
346
  return this.httpClient.request({
347
347
  method: "POST",
348
- path: "/v1/ingest/text",
349
- body: request
348
+ path: "/v1/ingest",
349
+ body: {
350
+ type: "text",
351
+ content: request.text,
352
+ projectId: request.projectId,
353
+ metadata: request.metadata || {}
354
+ }
355
+ });
356
+ }
357
+ /**
358
+ * Ingest content from a URL
359
+ * @param url - URL to ingest from
360
+ * @param projectId - Project ID
361
+ * @param metadata - Optional metadata
362
+ * @returns Ingestion response with job details
363
+ */
364
+ async url(url, projectId, metadata) {
365
+ if (!url || url.trim().length === 0) {
366
+ throw new ValidationError(
367
+ "URL cannot be empty",
368
+ [{ field: "url", message: "URL is required and cannot be empty" }]
369
+ );
370
+ }
371
+ if (!projectId || projectId.trim().length === 0) {
372
+ throw new ValidationError(
373
+ "Project ID is required",
374
+ [{ field: "projectId", message: "Project ID is required" }]
375
+ );
376
+ }
377
+ return this.httpClient.request({
378
+ method: "POST",
379
+ path: "/v1/ingest",
380
+ body: {
381
+ type: "url",
382
+ url,
383
+ projectId,
384
+ metadata: metadata || {}
385
+ }
386
+ });
387
+ }
388
+ /**
389
+ * Get the status of an ingestion job
390
+ * @param jobId - Job ID returned from ingest
391
+ * @param projectId - Project ID
392
+ * @returns Job status information
393
+ */
394
+ async getJob(jobId, projectId) {
395
+ if (!jobId || jobId.trim().length === 0) {
396
+ throw new ValidationError(
397
+ "Job ID is required",
398
+ [{ field: "jobId", message: "Job ID is required" }]
399
+ );
400
+ }
401
+ if (!projectId || projectId.trim().length === 0) {
402
+ throw new ValidationError(
403
+ "Project ID is required",
404
+ [{ field: "projectId", message: "Project ID is required" }]
405
+ );
406
+ }
407
+ const response = await this.httpClient.request({
408
+ method: "GET",
409
+ path: `/v1/jobs/${jobId}`,
410
+ query: { projectId }
350
411
  });
412
+ return response.data || response;
351
413
  }
352
414
  };
353
415
  }
package/dist/index.d.cts CHANGED
@@ -163,13 +163,13 @@ interface SearchRequest {
163
163
  query: string;
164
164
  /** Project ID to search in */
165
165
  projectId: string;
166
- /** Maximum number of results to return (default: 10) */
166
+ /** Maximum number of results to return (default: 10 - supermemory production default) */
167
167
  limit?: number;
168
168
  /** Filter criteria for search */
169
169
  filter?: Record<string, any>;
170
- /** Minimum relevance score threshold (0-1) */
170
+ /** Minimum relevance score threshold (0-1, default: 0.6 - supermemory production default for broad recall) */
171
171
  threshold?: number;
172
- /** Enable query rewriting (default: true) */
172
+ /** Enable query rewriting (default: false - adds ~400ms latency) */
173
173
  enableQueryRewriting?: boolean;
174
174
  /** Enable entity expansion search (default: false) */
175
175
  enableEntityExpansion?: boolean;
@@ -177,7 +177,7 @@ interface SearchRequest {
177
177
  enableGraphConnectivity?: boolean;
178
178
  /** Enable semantic deduplication (default: false) */
179
179
  enableSemanticDedup?: boolean;
180
- /** Reranking strategy: 'none', 'cross-encoder', 'llm' (default: 'none') */
180
+ /** Reranking strategy: 'none', 'cross-encoder', 'llm' (default: 'none' - adds latency) */
181
181
  rerankingStrategy?: 'none' | 'cross-encoder' | 'llm';
182
182
  /** Custom fusion weights for multi-method retrieval */
183
183
  fusionWeights?: {
@@ -232,9 +232,9 @@ interface IngestFileRequest {
232
232
  projectId: string;
233
233
  /** Optional metadata to associate with ingested memories */
234
234
  metadata?: Record<string, any>;
235
- /** Chunk size for splitting the file (default: 1000) */
235
+ /** Chunk size for splitting the file (default: 512 tokens - supermemory production default) */
236
236
  chunkSize?: number;
237
- /** Overlap between chunks (default: 200) */
237
+ /** Overlap between chunks (default: 10% - supermemory production default) */
238
238
  chunkOverlap?: number;
239
239
  }
240
240
  /**
@@ -247,9 +247,9 @@ interface IngestTextRequest {
247
247
  projectId: string;
248
248
  /** Optional metadata to associate with ingested memories */
249
249
  metadata?: Record<string, any>;
250
- /** Chunk size for splitting the text (default: 1000) */
250
+ /** Chunk size for splitting the text (default: 512 tokens - supermemory production default) */
251
251
  chunkSize?: number;
252
- /** Overlap between chunks (default: 200) */
252
+ /** Overlap between chunks (default: 10% - supermemory production default) */
253
253
  chunkOverlap?: number;
254
254
  }
255
255
  /**
@@ -278,11 +278,11 @@ interface RouterRequest {
278
278
  messages: Message[];
279
279
  /** Project ID for memory context */
280
280
  projectId: string;
281
- /** Model to use (optional) */
281
+ /** Model to use (default: 'gpt-4o-mini' - supermemory production default) */
282
282
  model?: string;
283
- /** Temperature for generation (0-2, default: 1) */
283
+ /** Temperature for generation (0-2, default: 0.7 - supermemory production default) */
284
284
  temperature?: number;
285
- /** Maximum tokens to generate */
285
+ /** Maximum tokens to generate (default: 2000 - supermemory production default) */
286
286
  maxTokens?: number;
287
287
  /** Whether to stream the response */
288
288
  stream?: boolean;
@@ -644,9 +644,18 @@ declare class SearchResource {
644
644
  private httpClient;
645
645
  constructor(httpClient: HTTPClient);
646
646
  /**
647
- * Search memories
647
+ * Search memories using the unified /v1/search endpoint with hybrid retrieval.
648
+ *
649
+ * This uses the app's full retrieval pipeline with:
650
+ * - Vector similarity search
651
+ * - BM25 keyword search
652
+ * - Recency scoring
653
+ * - Graph connectivity (optional)
654
+ * - Entity expansion (optional)
655
+ * - LLM/Cross-encoder reranking (optional)
656
+ *
648
657
  * @param request - Search request
649
- * @returns Search results
658
+ * @returns Search results with memory pack structure
650
659
  */
651
660
  search(request: SearchRequest): Promise<SearchResponse>;
652
661
  }
@@ -669,6 +678,21 @@ declare class IngestResource {
669
678
  * @returns Ingestion response with created memory IDs
670
679
  */
671
680
  text(request: IngestTextRequest): Promise<IngestResponse>;
681
+ /**
682
+ * Ingest content from a URL
683
+ * @param url - URL to ingest from
684
+ * @param projectId - Project ID
685
+ * @param metadata - Optional metadata
686
+ * @returns Ingestion response with job details
687
+ */
688
+ url(url: string, projectId: string, metadata?: Record<string, any>): Promise<IngestResponse>;
689
+ /**
690
+ * Get the status of an ingestion job
691
+ * @param jobId - Job ID returned from ingest
692
+ * @param projectId - Project ID
693
+ * @returns Job status information
694
+ */
695
+ getJob(jobId: string, projectId: string): Promise<any>;
672
696
  }
673
697
 
674
698
  /**
package/dist/index.d.ts CHANGED
@@ -163,13 +163,13 @@ interface SearchRequest {
163
163
  query: string;
164
164
  /** Project ID to search in */
165
165
  projectId: string;
166
- /** Maximum number of results to return (default: 10) */
166
+ /** Maximum number of results to return (default: 10 - supermemory production default) */
167
167
  limit?: number;
168
168
  /** Filter criteria for search */
169
169
  filter?: Record<string, any>;
170
- /** Minimum relevance score threshold (0-1) */
170
+ /** Minimum relevance score threshold (0-1, default: 0.6 - supermemory production default for broad recall) */
171
171
  threshold?: number;
172
- /** Enable query rewriting (default: true) */
172
+ /** Enable query rewriting (default: false - adds ~400ms latency) */
173
173
  enableQueryRewriting?: boolean;
174
174
  /** Enable entity expansion search (default: false) */
175
175
  enableEntityExpansion?: boolean;
@@ -177,7 +177,7 @@ interface SearchRequest {
177
177
  enableGraphConnectivity?: boolean;
178
178
  /** Enable semantic deduplication (default: false) */
179
179
  enableSemanticDedup?: boolean;
180
- /** Reranking strategy: 'none', 'cross-encoder', 'llm' (default: 'none') */
180
+ /** Reranking strategy: 'none', 'cross-encoder', 'llm' (default: 'none' - adds latency) */
181
181
  rerankingStrategy?: 'none' | 'cross-encoder' | 'llm';
182
182
  /** Custom fusion weights for multi-method retrieval */
183
183
  fusionWeights?: {
@@ -232,9 +232,9 @@ interface IngestFileRequest {
232
232
  projectId: string;
233
233
  /** Optional metadata to associate with ingested memories */
234
234
  metadata?: Record<string, any>;
235
- /** Chunk size for splitting the file (default: 1000) */
235
+ /** Chunk size for splitting the file (default: 512 tokens - supermemory production default) */
236
236
  chunkSize?: number;
237
- /** Overlap between chunks (default: 200) */
237
+ /** Overlap between chunks (default: 10% - supermemory production default) */
238
238
  chunkOverlap?: number;
239
239
  }
240
240
  /**
@@ -247,9 +247,9 @@ interface IngestTextRequest {
247
247
  projectId: string;
248
248
  /** Optional metadata to associate with ingested memories */
249
249
  metadata?: Record<string, any>;
250
- /** Chunk size for splitting the text (default: 1000) */
250
+ /** Chunk size for splitting the text (default: 512 tokens - supermemory production default) */
251
251
  chunkSize?: number;
252
- /** Overlap between chunks (default: 200) */
252
+ /** Overlap between chunks (default: 10% - supermemory production default) */
253
253
  chunkOverlap?: number;
254
254
  }
255
255
  /**
@@ -278,11 +278,11 @@ interface RouterRequest {
278
278
  messages: Message[];
279
279
  /** Project ID for memory context */
280
280
  projectId: string;
281
- /** Model to use (optional) */
281
+ /** Model to use (default: 'gpt-4o-mini' - supermemory production default) */
282
282
  model?: string;
283
- /** Temperature for generation (0-2, default: 1) */
283
+ /** Temperature for generation (0-2, default: 0.7 - supermemory production default) */
284
284
  temperature?: number;
285
- /** Maximum tokens to generate */
285
+ /** Maximum tokens to generate (default: 2000 - supermemory production default) */
286
286
  maxTokens?: number;
287
287
  /** Whether to stream the response */
288
288
  stream?: boolean;
@@ -644,9 +644,18 @@ declare class SearchResource {
644
644
  private httpClient;
645
645
  constructor(httpClient: HTTPClient);
646
646
  /**
647
- * Search memories
647
+ * Search memories using the unified /v1/search endpoint with hybrid retrieval.
648
+ *
649
+ * This uses the app's full retrieval pipeline with:
650
+ * - Vector similarity search
651
+ * - BM25 keyword search
652
+ * - Recency scoring
653
+ * - Graph connectivity (optional)
654
+ * - Entity expansion (optional)
655
+ * - LLM/Cross-encoder reranking (optional)
656
+ *
648
657
  * @param request - Search request
649
- * @returns Search results
658
+ * @returns Search results with memory pack structure
650
659
  */
651
660
  search(request: SearchRequest): Promise<SearchResponse>;
652
661
  }
@@ -669,6 +678,21 @@ declare class IngestResource {
669
678
  * @returns Ingestion response with created memory IDs
670
679
  */
671
680
  text(request: IngestTextRequest): Promise<IngestResponse>;
681
+ /**
682
+ * Ingest content from a URL
683
+ * @param url - URL to ingest from
684
+ * @param projectId - Project ID
685
+ * @param metadata - Optional metadata
686
+ * @returns Ingestion response with job details
687
+ */
688
+ url(url: string, projectId: string, metadata?: Record<string, any>): Promise<IngestResponse>;
689
+ /**
690
+ * Get the status of an ingestion job
691
+ * @param jobId - Job ID returned from ingest
692
+ * @param projectId - Project ID
693
+ * @returns Job status information
694
+ */
695
+ getJob(jobId: string, projectId: string): Promise<any>;
672
696
  }
673
697
 
674
698
  /**
package/dist/index.js CHANGED
@@ -220,9 +220,18 @@ var init_search = __esm({
220
220
  this.httpClient = httpClient;
221
221
  }
222
222
  /**
223
- * Search memories
223
+ * Search memories using the unified /v1/search endpoint with hybrid retrieval.
224
+ *
225
+ * This uses the app's full retrieval pipeline with:
226
+ * - Vector similarity search
227
+ * - BM25 keyword search
228
+ * - Recency scoring
229
+ * - Graph connectivity (optional)
230
+ * - Entity expansion (optional)
231
+ * - LLM/Cross-encoder reranking (optional)
232
+ *
224
233
  * @param request - Search request
225
- * @returns Search results
234
+ * @returns Search results with memory pack structure
226
235
  */
227
236
  async search(request) {
228
237
  if (!request.query || request.query.trim().length === 0) {
@@ -237,42 +246,36 @@ var init_search = __esm({
237
246
  [{ field: "projectId", message: "Project ID is required" }]
238
247
  );
239
248
  }
240
- const query = {
241
- q: request.query,
242
- projectId: request.projectId
249
+ const body = {
250
+ query: request.query,
251
+ project_id: request.projectId,
252
+ include_text_format: true
243
253
  };
244
254
  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);
255
+ body.limit = request.limit;
252
256
  }
253
- if (request.enableQueryRewriting !== void 0) {
254
- query.enableQueryRewriting = request.enableQueryRewriting.toString();
255
- }
256
- if (request.enableEntityExpansion !== void 0) {
257
- query.enableEntityExpansion = request.enableEntityExpansion.toString();
258
- }
259
- if (request.enableGraphConnectivity !== void 0) {
260
- query.enableGraphConnectivity = request.enableGraphConnectivity.toString();
261
- }
262
- if (request.enableSemanticDedup !== void 0) {
263
- query.enableSemanticDedup = request.enableSemanticDedup.toString();
264
- }
265
- if (request.rerankingStrategy) {
266
- query.rerankingStrategy = request.rerankingStrategy;
267
- }
268
- if (request.fusionWeights) {
269
- query.fusionWeights = JSON.stringify(request.fusionWeights);
270
- }
271
- return this.httpClient.request({
272
- method: "GET",
257
+ body.rerank_strategy = request.rerankingStrategy || "cross-encoder";
258
+ const response = await this.httpClient.request({
259
+ method: "POST",
273
260
  path: "/v1/search",
274
- query
261
+ body
275
262
  });
263
+ const memoryPack = response.memory_pack || {};
264
+ const results = [];
265
+ for (const memoryType of ["facts", "preferences", "entities", "sources"]) {
266
+ const items = memoryPack[memoryType] || [];
267
+ for (const item of items) {
268
+ results.push({
269
+ memory: item,
270
+ score: item.score || 1,
271
+ highlights: item.highlights || []
272
+ });
273
+ }
274
+ }
275
+ return {
276
+ results,
277
+ total: results.length
278
+ };
276
279
  }
277
280
  };
278
281
  }
@@ -310,18 +313,15 @@ var init_ingest = __esm({
310
313
  [{ field: "projectId", message: "Project ID is required" }]
311
314
  );
312
315
  }
313
- const body = {
314
- projectId: request.projectId,
315
- metadata: request.metadata,
316
- chunkSize: request.chunkSize,
317
- chunkOverlap: request.chunkOverlap,
318
- // In a real implementation, you'd convert the file to base64 or use FormData
319
- file: request.file
320
- };
321
316
  return this.httpClient.request({
322
317
  method: "POST",
323
- path: "/v1/ingest/file",
324
- body
318
+ path: "/v1/ingest",
319
+ body: {
320
+ type: "pdf",
321
+ projectId: request.projectId,
322
+ metadata: request.metadata || {},
323
+ file: request.file
324
+ }
325
325
  });
326
326
  }
327
327
  /**
@@ -344,9 +344,71 @@ var init_ingest = __esm({
344
344
  }
345
345
  return this.httpClient.request({
346
346
  method: "POST",
347
- path: "/v1/ingest/text",
348
- body: request
347
+ path: "/v1/ingest",
348
+ body: {
349
+ type: "text",
350
+ content: request.text,
351
+ projectId: request.projectId,
352
+ metadata: request.metadata || {}
353
+ }
354
+ });
355
+ }
356
+ /**
357
+ * Ingest content from a URL
358
+ * @param url - URL to ingest from
359
+ * @param projectId - Project ID
360
+ * @param metadata - Optional metadata
361
+ * @returns Ingestion response with job details
362
+ */
363
+ async url(url, projectId, metadata) {
364
+ if (!url || url.trim().length === 0) {
365
+ throw new ValidationError(
366
+ "URL cannot be empty",
367
+ [{ field: "url", message: "URL is required and cannot be empty" }]
368
+ );
369
+ }
370
+ if (!projectId || projectId.trim().length === 0) {
371
+ throw new ValidationError(
372
+ "Project ID is required",
373
+ [{ field: "projectId", message: "Project ID is required" }]
374
+ );
375
+ }
376
+ return this.httpClient.request({
377
+ method: "POST",
378
+ path: "/v1/ingest",
379
+ body: {
380
+ type: "url",
381
+ url,
382
+ projectId,
383
+ metadata: metadata || {}
384
+ }
385
+ });
386
+ }
387
+ /**
388
+ * Get the status of an ingestion job
389
+ * @param jobId - Job ID returned from ingest
390
+ * @param projectId - Project ID
391
+ * @returns Job status information
392
+ */
393
+ async getJob(jobId, projectId) {
394
+ if (!jobId || jobId.trim().length === 0) {
395
+ throw new ValidationError(
396
+ "Job ID is required",
397
+ [{ field: "jobId", message: "Job ID is required" }]
398
+ );
399
+ }
400
+ if (!projectId || projectId.trim().length === 0) {
401
+ throw new ValidationError(
402
+ "Project ID is required",
403
+ [{ field: "projectId", message: "Project ID is required" }]
404
+ );
405
+ }
406
+ const response = await this.httpClient.request({
407
+ method: "GET",
408
+ path: `/v1/jobs/${jobId}`,
409
+ query: { projectId }
349
410
  });
411
+ return response.data || response;
350
412
  }
351
413
  };
352
414
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memorylayerai/sdk",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "private": false,
5
5
  "description": "Official Node.js/TypeScript SDK for MemoryLayer",
6
6
  "main": "dist/index.js",
@@ -29,21 +29,16 @@ export class IngestResource {
29
29
  );
30
30
  }
31
31
 
32
- // For file uploads, we need to use FormData
33
- // This is a simplified implementation - in production, you'd handle multipart/form-data properly
34
- const body = {
35
- projectId: request.projectId,
36
- metadata: request.metadata,
37
- chunkSize: request.chunkSize,
38
- chunkOverlap: request.chunkOverlap,
39
- // In a real implementation, you'd convert the file to base64 or use FormData
40
- file: request.file,
41
- };
42
-
32
+ // Use unified /v1/ingest endpoint matching the app
43
33
  return this.httpClient.request<IngestResponse>({
44
34
  method: 'POST',
45
- path: '/v1/ingest/file',
46
- body,
35
+ path: '/v1/ingest',
36
+ body: {
37
+ type: 'pdf',
38
+ projectId: request.projectId,
39
+ metadata: request.metadata || {},
40
+ file: request.file,
41
+ },
47
42
  });
48
43
  }
49
44
 
@@ -68,10 +63,82 @@ export class IngestResource {
68
63
  );
69
64
  }
70
65
 
66
+ // Use unified /v1/ingest endpoint matching the app
71
67
  return this.httpClient.request<IngestResponse>({
72
68
  method: 'POST',
73
- path: '/v1/ingest/text',
74
- body: request,
69
+ path: '/v1/ingest',
70
+ body: {
71
+ type: 'text',
72
+ content: request.text,
73
+ projectId: request.projectId,
74
+ metadata: request.metadata || {},
75
+ },
75
76
  });
76
77
  }
78
+
79
+ /**
80
+ * Ingest content from a URL
81
+ * @param url - URL to ingest from
82
+ * @param projectId - Project ID
83
+ * @param metadata - Optional metadata
84
+ * @returns Ingestion response with job details
85
+ */
86
+ async url(url: string, projectId: string, metadata?: Record<string, any>): Promise<IngestResponse> {
87
+ // Validate request
88
+ if (!url || url.trim().length === 0) {
89
+ throw new ValidationError(
90
+ 'URL cannot be empty',
91
+ [{ field: 'url', message: 'URL is required and cannot be empty' }]
92
+ );
93
+ }
94
+
95
+ if (!projectId || projectId.trim().length === 0) {
96
+ throw new ValidationError(
97
+ 'Project ID is required',
98
+ [{ field: 'projectId', message: 'Project ID is required' }]
99
+ );
100
+ }
101
+
102
+ // Use unified /v1/ingest endpoint matching the app
103
+ return this.httpClient.request<IngestResponse>({
104
+ method: 'POST',
105
+ path: '/v1/ingest',
106
+ body: {
107
+ type: 'url',
108
+ url,
109
+ projectId,
110
+ metadata: metadata || {},
111
+ },
112
+ });
113
+ }
114
+
115
+ /**
116
+ * Get the status of an ingestion job
117
+ * @param jobId - Job ID returned from ingest
118
+ * @param projectId - Project ID
119
+ * @returns Job status information
120
+ */
121
+ async getJob(jobId: string, projectId: string): Promise<any> {
122
+ if (!jobId || jobId.trim().length === 0) {
123
+ throw new ValidationError(
124
+ 'Job ID is required',
125
+ [{ field: 'jobId', message: 'Job ID is required' }]
126
+ );
127
+ }
128
+
129
+ if (!projectId || projectId.trim().length === 0) {
130
+ throw new ValidationError(
131
+ 'Project ID is required',
132
+ [{ field: 'projectId', message: 'Project ID is required' }]
133
+ );
134
+ }
135
+
136
+ const response = await this.httpClient.request<any>({
137
+ method: 'GET',
138
+ path: `/v1/jobs/${jobId}`,
139
+ query: { projectId },
140
+ });
141
+
142
+ return response.data || response;
143
+ }
77
144
  }
@@ -9,9 +9,18 @@ export class SearchResource {
9
9
  constructor(private httpClient: HTTPClient) {}
10
10
 
11
11
  /**
12
- * Search memories
12
+ * Search memories using the unified /v1/search endpoint with hybrid retrieval.
13
+ *
14
+ * This uses the app's full retrieval pipeline with:
15
+ * - Vector similarity search
16
+ * - BM25 keyword search
17
+ * - Recency scoring
18
+ * - Graph connectivity (optional)
19
+ * - Entity expansion (optional)
20
+ * - LLM/Cross-encoder reranking (optional)
21
+ *
13
22
  * @param request - Search request
14
- * @returns Search results
23
+ * @returns Search results with memory pack structure
15
24
  */
16
25
  async search(request: SearchRequest): Promise<SearchResponse> {
17
26
  // Validate request
@@ -29,52 +38,46 @@ export class SearchResource {
29
38
  );
30
39
  }
31
40
 
32
- const query: Record<string, string> = {
33
- q: request.query,
34
- projectId: request.projectId,
41
+ // Build request body matching the app's POST /v1/search endpoint
42
+ const body: any = {
43
+ query: request.query,
44
+ project_id: request.projectId,
45
+ include_text_format: true,
35
46
  };
36
47
 
37
48
  if (request.limit !== undefined) {
38
- query.limit = request.limit.toString();
49
+ body.limit = request.limit;
39
50
  }
40
51
 
41
- if (request.threshold !== undefined) {
42
- query.threshold = request.threshold.toString();
43
- }
44
-
45
- if (request.filter) {
46
- query.filter = JSON.stringify(request.filter);
47
- }
48
-
49
- // New advanced retrieval options
50
- if (request.enableQueryRewriting !== undefined) {
51
- query.enableQueryRewriting = request.enableQueryRewriting.toString();
52
- }
53
-
54
- if (request.enableEntityExpansion !== undefined) {
55
- query.enableEntityExpansion = request.enableEntityExpansion.toString();
56
- }
57
-
58
- if (request.enableGraphConnectivity !== undefined) {
59
- query.enableGraphConnectivity = request.enableGraphConnectivity.toString();
60
- }
52
+ // Use rerank_strategy to match app's parameter name
53
+ body.rerank_strategy = request.rerankingStrategy || 'cross-encoder';
61
54
 
62
- if (request.enableSemanticDedup !== undefined) {
63
- query.enableSemanticDedup = request.enableSemanticDedup.toString();
64
- }
55
+ // Use POST method to match the app's endpoint
56
+ const response = await this.httpClient.request<any>({
57
+ method: 'POST',
58
+ path: '/v1/search',
59
+ body,
60
+ });
65
61
 
66
- if (request.rerankingStrategy) {
67
- query.rerankingStrategy = request.rerankingStrategy;
68
- }
62
+ // Parse response from memory_pack format
63
+ const memoryPack = response.memory_pack || {};
64
+ const results = [];
69
65
 
70
- if (request.fusionWeights) {
71
- query.fusionWeights = JSON.stringify(request.fusionWeights);
66
+ // Extract memories from memory pack structure
67
+ for (const memoryType of ['facts', 'preferences', 'entities', 'sources']) {
68
+ const items = memoryPack[memoryType] || [];
69
+ for (const item of items) {
70
+ results.push({
71
+ memory: item,
72
+ score: item.score || 1.0,
73
+ highlights: item.highlights || [],
74
+ });
75
+ }
72
76
  }
73
77
 
74
- return this.httpClient.request<SearchResponse>({
75
- method: 'GET',
76
- path: '/v1/search',
77
- query,
78
- });
78
+ return {
79
+ results,
80
+ total: results.length,
81
+ };
79
82
  }
80
83
  }
package/src/types.ts CHANGED
@@ -60,13 +60,13 @@ export interface SearchRequest {
60
60
  query: string;
61
61
  /** Project ID to search in */
62
62
  projectId: string;
63
- /** Maximum number of results to return (default: 10) */
63
+ /** Maximum number of results to return (default: 10 - supermemory production default) */
64
64
  limit?: number;
65
65
  /** Filter criteria for search */
66
66
  filter?: Record<string, any>;
67
- /** Minimum relevance score threshold (0-1) */
67
+ /** Minimum relevance score threshold (0-1, default: 0.6 - supermemory production default for broad recall) */
68
68
  threshold?: number;
69
- /** Enable query rewriting (default: true) */
69
+ /** Enable query rewriting (default: false - adds ~400ms latency) */
70
70
  enableQueryRewriting?: boolean;
71
71
  /** Enable entity expansion search (default: false) */
72
72
  enableEntityExpansion?: boolean;
@@ -74,7 +74,7 @@ export interface SearchRequest {
74
74
  enableGraphConnectivity?: boolean;
75
75
  /** Enable semantic deduplication (default: false) */
76
76
  enableSemanticDedup?: boolean;
77
- /** Reranking strategy: 'none', 'cross-encoder', 'llm' (default: 'none') */
77
+ /** Reranking strategy: 'none', 'cross-encoder', 'llm' (default: 'none' - adds latency) */
78
78
  rerankingStrategy?: 'none' | 'cross-encoder' | 'llm';
79
79
  /** Custom fusion weights for multi-method retrieval */
80
80
  fusionWeights?: {
@@ -132,9 +132,9 @@ export interface IngestFileRequest {
132
132
  projectId: string;
133
133
  /** Optional metadata to associate with ingested memories */
134
134
  metadata?: Record<string, any>;
135
- /** Chunk size for splitting the file (default: 1000) */
135
+ /** Chunk size for splitting the file (default: 512 tokens - supermemory production default) */
136
136
  chunkSize?: number;
137
- /** Overlap between chunks (default: 200) */
137
+ /** Overlap between chunks (default: 10% - supermemory production default) */
138
138
  chunkOverlap?: number;
139
139
  }
140
140
 
@@ -148,9 +148,9 @@ export interface IngestTextRequest {
148
148
  projectId: string;
149
149
  /** Optional metadata to associate with ingested memories */
150
150
  metadata?: Record<string, any>;
151
- /** Chunk size for splitting the text (default: 1000) */
151
+ /** Chunk size for splitting the text (default: 512 tokens - supermemory production default) */
152
152
  chunkSize?: number;
153
- /** Overlap between chunks (default: 200) */
153
+ /** Overlap between chunks (default: 10% - supermemory production default) */
154
154
  chunkOverlap?: number;
155
155
  }
156
156
 
@@ -182,11 +182,11 @@ export interface RouterRequest {
182
182
  messages: Message[];
183
183
  /** Project ID for memory context */
184
184
  projectId: string;
185
- /** Model to use (optional) */
185
+ /** Model to use (default: 'gpt-4o-mini' - supermemory production default) */
186
186
  model?: string;
187
- /** Temperature for generation (0-2, default: 1) */
187
+ /** Temperature for generation (0-2, default: 0.7 - supermemory production default) */
188
188
  temperature?: number;
189
- /** Maximum tokens to generate */
189
+ /** Maximum tokens to generate (default: 2000 - supermemory production default) */
190
190
  maxTokens?: number;
191
191
  /** Whether to stream the response */
192
192
  stream?: boolean;