@memorylayerai/sdk 0.4.0 → 0.6.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memorylayerai/sdk",
3
- "version": "0.4.0",
3
+ "version": "0.6.1",
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
@@ -1,3 +1,36 @@
1
+ /**
2
+ * Model provider options for multi-provider support
3
+ * Allows overriding org-level settings at request time
4
+ *
5
+ * Supported providers:
6
+ * - 'openai': OpenAI GPT models and text-embedding models
7
+ * - 'google': Google Gemini models and text-embedding-004
8
+ * - 'anthropic': Anthropic Claude models (uses OpenAI embeddings for vector search)
9
+ */
10
+ export type ModelProvider = 'openai' | 'google' | 'anthropic';
11
+
12
+ /**
13
+ * Model settings for request-level override
14
+ */
15
+ export interface ModelSettings {
16
+ /** Model provider: 'openai', 'google', or 'anthropic' */
17
+ provider?: ModelProvider;
18
+ /**
19
+ * Embedding model to use:
20
+ * - OpenAI: 'text-embedding-3-small', 'text-embedding-3-large', 'text-embedding-ada-002'
21
+ * - Google: 'text-embedding-004'
22
+ * - Anthropic: Uses OpenAI embeddings (specify OpenAI model)
23
+ */
24
+ embeddingModel?: string;
25
+ /**
26
+ * Language model for extraction/generation:
27
+ * - OpenAI: 'gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'o1', 'o1-mini'
28
+ * - Google: 'gemini-2.5-flash', 'gemini-2.5-pro', 'gemini-2.0-flash'
29
+ * - Anthropic: 'claude-3-5-sonnet-20241022', 'claude-3-opus-20240229', 'claude-3-haiku-20240307'
30
+ */
31
+ languageModel?: string;
32
+ }
33
+
1
34
  /**
2
35
  * Memory object
3
36
  */
@@ -60,13 +93,13 @@ export interface SearchRequest {
60
93
  query: string;
61
94
  /** Project ID to search in */
62
95
  projectId: string;
63
- /** Maximum number of results to return (default: 10) */
96
+ /** Maximum number of results to return (default: 10 - supermemory production default) */
64
97
  limit?: number;
65
98
  /** Filter criteria for search */
66
99
  filter?: Record<string, any>;
67
- /** Minimum relevance score threshold (0-1) */
100
+ /** Minimum relevance score threshold (0-1, default: 0.6 - supermemory production default for broad recall) */
68
101
  threshold?: number;
69
- /** Enable query rewriting (default: true) */
102
+ /** Enable query rewriting (default: false - adds ~400ms latency) */
70
103
  enableQueryRewriting?: boolean;
71
104
  /** Enable entity expansion search (default: false) */
72
105
  enableEntityExpansion?: boolean;
@@ -74,7 +107,7 @@ export interface SearchRequest {
74
107
  enableGraphConnectivity?: boolean;
75
108
  /** Enable semantic deduplication (default: false) */
76
109
  enableSemanticDedup?: boolean;
77
- /** Reranking strategy: 'none', 'cross-encoder', 'llm' (default: 'none') */
110
+ /** Reranking strategy: 'none', 'cross-encoder', 'llm' (default: 'none' - adds latency) */
78
111
  rerankingStrategy?: 'none' | 'cross-encoder' | 'llm';
79
112
  /** Custom fusion weights for multi-method retrieval */
80
113
  fusionWeights?: {
@@ -84,6 +117,8 @@ export interface SearchRequest {
84
117
  entity?: number;
85
118
  graph?: number;
86
119
  };
120
+ /** Model settings override (optional - uses org settings if not specified) */
121
+ modelSettings?: ModelSettings;
87
122
  }
88
123
 
89
124
  /**
@@ -132,10 +167,12 @@ export interface IngestFileRequest {
132
167
  projectId: string;
133
168
  /** Optional metadata to associate with ingested memories */
134
169
  metadata?: Record<string, any>;
135
- /** Chunk size for splitting the file (default: 1000) */
170
+ /** Chunk size for splitting the file (default: 512 tokens - supermemory production default) */
136
171
  chunkSize?: number;
137
- /** Overlap between chunks (default: 200) */
172
+ /** Overlap between chunks (default: 10% - supermemory production default) */
138
173
  chunkOverlap?: number;
174
+ /** Model settings override (optional - uses org settings if not specified) */
175
+ modelSettings?: ModelSettings;
139
176
  }
140
177
 
141
178
  /**
@@ -148,10 +185,12 @@ export interface IngestTextRequest {
148
185
  projectId: string;
149
186
  /** Optional metadata to associate with ingested memories */
150
187
  metadata?: Record<string, any>;
151
- /** Chunk size for splitting the text (default: 1000) */
188
+ /** Chunk size for splitting the text (default: 512 tokens - supermemory production default) */
152
189
  chunkSize?: number;
153
- /** Overlap between chunks (default: 200) */
190
+ /** Overlap between chunks (default: 10% - supermemory production default) */
154
191
  chunkOverlap?: number;
192
+ /** Model settings override (optional - uses org settings if not specified) */
193
+ modelSettings?: ModelSettings;
155
194
  }
156
195
 
157
196
  /**
@@ -182,11 +221,11 @@ export interface RouterRequest {
182
221
  messages: Message[];
183
222
  /** Project ID for memory context */
184
223
  projectId: string;
185
- /** Model to use (optional) */
224
+ /** Model to use (default: 'gpt-4o-mini' - supermemory production default) */
186
225
  model?: string;
187
- /** Temperature for generation (0-2, default: 1) */
226
+ /** Temperature for generation (0-2, default: 0.7 - supermemory production default) */
188
227
  temperature?: number;
189
- /** Maximum tokens to generate */
228
+ /** Maximum tokens to generate (default: 2000 - supermemory production default) */
190
229
  maxTokens?: number;
191
230
  /** Whether to stream the response */
192
231
  stream?: boolean;