@sovant/sdk 1.0.5 → 1.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/CHANGELOG.md +101 -0
- package/LICENSE +1 -1
- package/README.md +395 -66
- package/dist/index.d.mts +431 -0
- package/dist/index.d.ts +426 -42
- package/dist/index.js +835 -102
- package/dist/index.mjs +809 -0
- package/package.json +49 -19
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,106 +1,839 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
NetworkError: () => NetworkError,
|
|
24
|
+
Sovant: () => Sovant,
|
|
25
|
+
SovantError: () => SovantError,
|
|
26
|
+
TimeoutError: () => TimeoutError,
|
|
27
|
+
default: () => index_default
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(index_exports);
|
|
30
|
+
|
|
31
|
+
// src/errors.ts
|
|
32
|
+
var SovantError = class _SovantError extends Error {
|
|
33
|
+
constructor(message, code, status, requestId) {
|
|
34
|
+
super(message);
|
|
35
|
+
this.name = "SovantError";
|
|
36
|
+
this.code = code;
|
|
37
|
+
this.status = status;
|
|
38
|
+
this.requestId = requestId;
|
|
39
|
+
if (Error.captureStackTrace) {
|
|
40
|
+
Error.captureStackTrace(this, _SovantError);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
static fromResponse(status, body, requestId) {
|
|
44
|
+
const statusCodeMap = {
|
|
45
|
+
400: "bad_request",
|
|
46
|
+
401: "unauthorized",
|
|
47
|
+
403: "forbidden",
|
|
48
|
+
404: "not_found",
|
|
49
|
+
409: "conflict",
|
|
50
|
+
422: "validation_error",
|
|
51
|
+
429: "rate_limited",
|
|
52
|
+
500: "internal_error",
|
|
53
|
+
502: "bad_gateway",
|
|
54
|
+
503: "service_unavailable",
|
|
55
|
+
504: "gateway_timeout"
|
|
56
|
+
};
|
|
57
|
+
let message = "An error occurred";
|
|
58
|
+
let code = statusCodeMap[status] || "unknown_error";
|
|
59
|
+
if (body?.error) {
|
|
60
|
+
if (typeof body.error === "string") {
|
|
61
|
+
message = body.error;
|
|
62
|
+
} else if (body.error.message) {
|
|
63
|
+
message = body.error.message;
|
|
64
|
+
code = body.error.code || code;
|
|
65
|
+
}
|
|
66
|
+
} else if (body?.message) {
|
|
67
|
+
message = body.message;
|
|
68
|
+
} else if (typeof body === "string") {
|
|
69
|
+
message = body;
|
|
70
|
+
}
|
|
71
|
+
return new _SovantError(message, code, status, requestId);
|
|
72
|
+
}
|
|
73
|
+
toJSON() {
|
|
74
|
+
return {
|
|
75
|
+
name: this.name,
|
|
76
|
+
message: this.message,
|
|
77
|
+
code: this.code,
|
|
78
|
+
status: this.status,
|
|
79
|
+
requestId: this.requestId
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
var NetworkError = class _NetworkError extends SovantError {
|
|
84
|
+
constructor(message, cause) {
|
|
85
|
+
super(message, "network_error");
|
|
86
|
+
this.name = "NetworkError";
|
|
87
|
+
if (Error.captureStackTrace) {
|
|
88
|
+
Error.captureStackTrace(this, _NetworkError);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
var TimeoutError = class _TimeoutError extends SovantError {
|
|
93
|
+
constructor(message = "Request timed out") {
|
|
94
|
+
super(message, "timeout");
|
|
95
|
+
this.name = "TimeoutError";
|
|
96
|
+
if (Error.captureStackTrace) {
|
|
97
|
+
Error.captureStackTrace(this, _TimeoutError);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// src/http.ts
|
|
103
|
+
var RETRY_STATUS_CODES = [429, 502, 503, 504];
|
|
104
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
105
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
106
|
+
var IDEMPOTENT_METHODS = ["GET", "DELETE"];
|
|
107
|
+
var HttpClient = class {
|
|
108
|
+
constructor(baseUrl, apiKey, globalOptions = {}) {
|
|
109
|
+
this.baseUrl = baseUrl;
|
|
110
|
+
this.apiKey = apiKey;
|
|
111
|
+
this.globalOptions = globalOptions;
|
|
112
|
+
}
|
|
113
|
+
async request(path, options = {}) {
|
|
114
|
+
const url = `${this.baseUrl}${path}`;
|
|
115
|
+
const timeout = options.timeout ?? this.globalOptions.timeout ?? DEFAULT_TIMEOUT;
|
|
116
|
+
const maxRetries = options.maxRetries ?? this.globalOptions.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
117
|
+
const debug = options.debug ?? this.globalOptions.debug ?? false;
|
|
118
|
+
const method = options.method || "GET";
|
|
119
|
+
const shouldRetry = IDEMPOTENT_METHODS.includes(method) || method === "GET";
|
|
120
|
+
let lastError;
|
|
121
|
+
let attempt = 0;
|
|
122
|
+
while (attempt <= maxRetries) {
|
|
123
|
+
try {
|
|
124
|
+
if (debug && attempt > 0) {
|
|
125
|
+
console.log(`[Sovant SDK] Retry attempt ${attempt} for ${method} ${url}`);
|
|
126
|
+
}
|
|
127
|
+
const response = await this.makeRequest(url, options, timeout, debug);
|
|
128
|
+
if (shouldRetry && RETRY_STATUS_CODES.includes(response.status) && attempt < maxRetries) {
|
|
129
|
+
const delay = this.calculateRetryDelay(attempt, response.headers["retry-after"]);
|
|
130
|
+
if (debug) {
|
|
131
|
+
console.log(`[Sovant SDK] Got ${response.status}, retrying after ${delay}ms...`);
|
|
132
|
+
}
|
|
133
|
+
await this.sleep(delay);
|
|
134
|
+
attempt++;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (response.status >= 400) {
|
|
138
|
+
throw SovantError.fromResponse(response.status, response.data, response.requestId);
|
|
139
|
+
}
|
|
140
|
+
return response;
|
|
141
|
+
} catch (error) {
|
|
142
|
+
lastError = error;
|
|
143
|
+
if (error instanceof SovantError) {
|
|
144
|
+
if (error.status && error.status < 500) {
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
if (!shouldRetry || attempt >= maxRetries) {
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (error instanceof TimeoutError || error instanceof NetworkError) {
|
|
152
|
+
if (shouldRetry && attempt < maxRetries) {
|
|
153
|
+
const delay = this.calculateRetryDelay(attempt);
|
|
154
|
+
if (debug) {
|
|
155
|
+
console.log(`[Sovant SDK] Network/Timeout error, retrying after ${delay}ms...`);
|
|
37
156
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}),
|
|
56
|
-
get: (id) => this.req(`/api/v1/memories/${encodeURIComponent(id)}`, {
|
|
57
|
-
method: "GET"
|
|
58
|
-
}),
|
|
59
|
-
search: (q) => {
|
|
60
|
-
const params = new URLSearchParams();
|
|
61
|
-
if (q.query)
|
|
62
|
-
params.append('query', q.query);
|
|
63
|
-
if (q.type)
|
|
64
|
-
params.append('type', q.type);
|
|
65
|
-
if (q.tags)
|
|
66
|
-
params.append('tags', q.tags.join(','));
|
|
67
|
-
if (q.thread_id)
|
|
68
|
-
params.append('thread_id', q.thread_id);
|
|
69
|
-
if (q.limit)
|
|
70
|
-
params.append('limit', q.limit.toString());
|
|
71
|
-
if (q.from_date)
|
|
72
|
-
params.append('from_date', q.from_date);
|
|
73
|
-
if (q.to_date)
|
|
74
|
-
params.append('to_date', q.to_date);
|
|
75
|
-
return this.req(`/api/v1/memory/search?${params.toString()}`, {
|
|
76
|
-
method: "GET"
|
|
77
|
-
});
|
|
78
|
-
},
|
|
79
|
-
update: (id, patch) => this.req(`/api/v1/memories/${encodeURIComponent(id)}`, {
|
|
80
|
-
method: "PATCH",
|
|
81
|
-
body: JSON.stringify({
|
|
82
|
-
content: patch.data ? (typeof patch.data === 'string' ? patch.data : JSON.stringify(patch.data)) : undefined,
|
|
83
|
-
type: patch.type,
|
|
84
|
-
ttl: patch.ttl,
|
|
85
|
-
tags: patch.tags,
|
|
86
|
-
metadata: patch.metadata
|
|
87
|
-
})
|
|
88
|
-
}),
|
|
89
|
-
delete: (id) => this.req(`/api/v1/memories/${encodeURIComponent(id)}`, {
|
|
90
|
-
method: "DELETE"
|
|
91
|
-
})
|
|
157
|
+
await this.sleep(delay);
|
|
158
|
+
attempt++;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
throw lastError || new NetworkError("Max retries exceeded");
|
|
166
|
+
}
|
|
167
|
+
async makeRequest(url, options, timeout, debug) {
|
|
168
|
+
const controller = new AbortController();
|
|
169
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
170
|
+
const headers = {
|
|
171
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
172
|
+
"Content-Type": "application/json",
|
|
173
|
+
...options.headers
|
|
92
174
|
};
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
175
|
+
const fetchOptions = {
|
|
176
|
+
method: options.method || "GET",
|
|
177
|
+
headers,
|
|
178
|
+
signal: controller.signal
|
|
179
|
+
};
|
|
180
|
+
if (options.body !== void 0 && options.method !== "GET" && options.method !== "HEAD") {
|
|
181
|
+
fetchOptions.body = typeof options.body === "string" ? options.body : JSON.stringify(options.body);
|
|
182
|
+
}
|
|
183
|
+
if (debug) {
|
|
184
|
+
console.log(`[Sovant SDK] ${fetchOptions.method} ${url}`);
|
|
185
|
+
if (options.body) {
|
|
186
|
+
console.log("[Sovant SDK] Body:", options.body);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
const response = await fetch(url, fetchOptions);
|
|
191
|
+
clearTimeout(timeoutId);
|
|
192
|
+
const responseHeaders = {};
|
|
193
|
+
response.headers.forEach((value, key) => {
|
|
194
|
+
responseHeaders[key.toLowerCase()] = value;
|
|
195
|
+
});
|
|
196
|
+
const requestId = responseHeaders["x-request-id"];
|
|
197
|
+
let data;
|
|
198
|
+
const contentType = responseHeaders["content-type"] || "";
|
|
199
|
+
if (response.status === 204) {
|
|
200
|
+
data = { ok: true };
|
|
201
|
+
} else if (contentType.includes("application/json")) {
|
|
202
|
+
const text = await response.text();
|
|
203
|
+
try {
|
|
204
|
+
data = text ? JSON.parse(text) : {};
|
|
205
|
+
} catch (e) {
|
|
206
|
+
data = { message: text };
|
|
207
|
+
}
|
|
208
|
+
} else {
|
|
209
|
+
data = await response.text();
|
|
210
|
+
}
|
|
211
|
+
if (debug) {
|
|
212
|
+
console.log(`[Sovant SDK] Response ${response.status}:`, data);
|
|
213
|
+
if (requestId) {
|
|
214
|
+
console.log(`[Sovant SDK] Request ID: ${requestId}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
data,
|
|
219
|
+
status: response.status,
|
|
220
|
+
headers: responseHeaders,
|
|
221
|
+
requestId
|
|
222
|
+
};
|
|
223
|
+
} catch (error) {
|
|
224
|
+
clearTimeout(timeoutId);
|
|
225
|
+
if (error instanceof Error) {
|
|
226
|
+
if (error.name === "AbortError") {
|
|
227
|
+
throw new TimeoutError(`Request timed out after ${timeout}ms`);
|
|
228
|
+
}
|
|
229
|
+
throw new NetworkError(`Network request failed: ${error.message}`, error);
|
|
230
|
+
}
|
|
231
|
+
throw error;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
calculateRetryDelay(attempt, retryAfter) {
|
|
235
|
+
if (retryAfter) {
|
|
236
|
+
const retryAfterNum = parseInt(retryAfter, 10);
|
|
237
|
+
if (!isNaN(retryAfterNum)) {
|
|
238
|
+
return retryAfterNum * 1e3;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const baseDelay = Math.min(1e3 * Math.pow(2, attempt), 1e4);
|
|
242
|
+
const jitter = Math.random() * baseDelay * 0.1;
|
|
243
|
+
return Math.floor(baseDelay + jitter);
|
|
244
|
+
}
|
|
245
|
+
sleep(ms) {
|
|
246
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
// src/sse-parser.ts
|
|
251
|
+
function parseSSE(buffer) {
|
|
252
|
+
const events = [];
|
|
253
|
+
const lines = buffer.split("\n");
|
|
254
|
+
let currentEvent = {};
|
|
255
|
+
let remaining = "";
|
|
256
|
+
for (let i = 0; i < lines.length; i++) {
|
|
257
|
+
const line = lines[i];
|
|
258
|
+
if (i === lines.length - 1 && !buffer.endsWith("\n")) {
|
|
259
|
+
remaining = line;
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
if (line.trim() === "") {
|
|
263
|
+
if (currentEvent.data !== void 0) {
|
|
264
|
+
events.push({
|
|
265
|
+
event: currentEvent.event,
|
|
266
|
+
data: currentEvent.data,
|
|
267
|
+
id: currentEvent.id,
|
|
268
|
+
retry: currentEvent.retry
|
|
269
|
+
});
|
|
270
|
+
currentEvent = {};
|
|
271
|
+
}
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (line.startsWith(":")) {
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
const colonIndex = line.indexOf(":");
|
|
278
|
+
if (colonIndex === -1) {
|
|
279
|
+
const field2 = line.trim();
|
|
280
|
+
if (field2 === "data") {
|
|
281
|
+
currentEvent.data = "";
|
|
282
|
+
}
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
const field = line.substring(0, colonIndex).trim();
|
|
286
|
+
let value = line.substring(colonIndex + 1);
|
|
287
|
+
if (value.startsWith(" ")) {
|
|
288
|
+
value = value.substring(1);
|
|
104
289
|
}
|
|
290
|
+
switch (field) {
|
|
291
|
+
case "event":
|
|
292
|
+
currentEvent.event = value;
|
|
293
|
+
break;
|
|
294
|
+
case "data":
|
|
295
|
+
if (currentEvent.data === void 0) {
|
|
296
|
+
currentEvent.data = value;
|
|
297
|
+
} else {
|
|
298
|
+
currentEvent.data += "\n" + value;
|
|
299
|
+
}
|
|
300
|
+
break;
|
|
301
|
+
case "id":
|
|
302
|
+
currentEvent.id = value;
|
|
303
|
+
break;
|
|
304
|
+
case "retry":
|
|
305
|
+
const retryTime = parseInt(value, 10);
|
|
306
|
+
if (!isNaN(retryTime)) {
|
|
307
|
+
currentEvent.retry = retryTime;
|
|
308
|
+
}
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return {
|
|
313
|
+
events,
|
|
314
|
+
buffer: remaining
|
|
315
|
+
};
|
|
105
316
|
}
|
|
106
|
-
|
|
317
|
+
|
|
318
|
+
// src/index.ts
|
|
319
|
+
var Sovant = class {
|
|
320
|
+
constructor(config) {
|
|
321
|
+
if (!config.apiKey) {
|
|
322
|
+
throw new Error("API key is required");
|
|
323
|
+
}
|
|
324
|
+
const baseUrl = (config.baseUrl || "https://sovant.ai").replace(/\/$/, "");
|
|
325
|
+
this.http = new HttpClient(baseUrl, config.apiKey, {
|
|
326
|
+
timeout: config.timeout || 3e4,
|
|
327
|
+
maxRetries: config.maxRetries || 3,
|
|
328
|
+
debug: config.debug || false
|
|
329
|
+
});
|
|
330
|
+
this.memory = new MemoryNamespace(this.http);
|
|
331
|
+
this.threads = new ThreadsNamespace(this.http);
|
|
332
|
+
this.chat = new ChatNamespace(this.http);
|
|
333
|
+
this.keys = new KeysNamespace(this.http);
|
|
334
|
+
this.recall = new RecallNamespace(this.http);
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
var MemoryNamespace = class {
|
|
338
|
+
constructor(http) {
|
|
339
|
+
this.http = http;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Create a new memory
|
|
343
|
+
*/
|
|
344
|
+
async create(input) {
|
|
345
|
+
const response = await this.http.request("/api/v1/memory", {
|
|
346
|
+
method: "POST",
|
|
347
|
+
body: input
|
|
348
|
+
});
|
|
349
|
+
const { ok, ...memory } = response.data;
|
|
350
|
+
return memory;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* List memories with optional filters
|
|
354
|
+
*/
|
|
355
|
+
async list(params) {
|
|
356
|
+
const queryParams = new URLSearchParams();
|
|
357
|
+
if (params?.limit !== void 0) queryParams.append("limit", params.limit.toString());
|
|
358
|
+
if (params?.offset !== void 0) queryParams.append("offset", params.offset.toString());
|
|
359
|
+
if (params?.type) queryParams.append("type", params.type);
|
|
360
|
+
if (params?.thread_id) queryParams.append("thread_id", params.thread_id);
|
|
361
|
+
if (params?.is_archived !== void 0) queryParams.append("is_archived", params.is_archived.toString());
|
|
362
|
+
if (params?.tags && params.tags.length > 0) {
|
|
363
|
+
params.tags.forEach((tag) => queryParams.append("tags", tag));
|
|
364
|
+
}
|
|
365
|
+
const query = queryParams.toString();
|
|
366
|
+
const path = query ? `/api/v1/memory?${query}` : "/api/v1/memory";
|
|
367
|
+
const response = await this.http.request(path, {
|
|
368
|
+
method: "GET"
|
|
369
|
+
});
|
|
370
|
+
return response.data;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Get a specific memory by ID
|
|
374
|
+
*/
|
|
375
|
+
async get(id) {
|
|
376
|
+
if (!id) throw new Error("Memory ID is required");
|
|
377
|
+
const response = await this.http.request(`/api/v1/memories/${id}`, {
|
|
378
|
+
method: "GET"
|
|
379
|
+
});
|
|
380
|
+
return response.data;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Update a memory (partial update using PATCH)
|
|
384
|
+
*/
|
|
385
|
+
async update(id, patch) {
|
|
386
|
+
if (!id) throw new Error("Memory ID is required");
|
|
387
|
+
const response = await this.http.request(`/api/v1/memories/${id}`, {
|
|
388
|
+
method: "PATCH",
|
|
389
|
+
body: patch
|
|
390
|
+
});
|
|
391
|
+
return response.data;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Replace a memory (full update using PUT)
|
|
395
|
+
*/
|
|
396
|
+
async put(id, body) {
|
|
397
|
+
if (!id) throw new Error("Memory ID is required");
|
|
398
|
+
if (!body.content) throw new Error("Content is required for PUT operation");
|
|
399
|
+
if (!body.type) throw new Error("Type is required for PUT operation");
|
|
400
|
+
const response = await this.http.request(`/api/v1/memories/${id}`, {
|
|
401
|
+
method: "PUT",
|
|
402
|
+
body
|
|
403
|
+
});
|
|
404
|
+
return response.data;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Delete a memory
|
|
408
|
+
*/
|
|
409
|
+
async delete(id) {
|
|
410
|
+
if (!id) throw new Error("Memory ID is required");
|
|
411
|
+
const response = await this.http.request(`/api/v1/memories/${id}`, {
|
|
412
|
+
method: "DELETE"
|
|
413
|
+
});
|
|
414
|
+
return response.data;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Search memories using semantic search or filters
|
|
418
|
+
*/
|
|
419
|
+
async search(params) {
|
|
420
|
+
const queryParams = new URLSearchParams();
|
|
421
|
+
if (params.query) queryParams.append("query", params.query);
|
|
422
|
+
if (params.type) queryParams.append("type", params.type);
|
|
423
|
+
if (params.limit !== void 0) queryParams.append("limit", params.limit.toString());
|
|
424
|
+
if (params.thread_id) queryParams.append("thread_id", params.thread_id);
|
|
425
|
+
if (params.from_date) queryParams.append("from_date", params.from_date);
|
|
426
|
+
if (params.to_date) queryParams.append("to_date", params.to_date);
|
|
427
|
+
if (params.tags && params.tags.length > 0) {
|
|
428
|
+
params.tags.forEach((tag) => queryParams.append("tags", tag));
|
|
429
|
+
}
|
|
430
|
+
const query = queryParams.toString();
|
|
431
|
+
const path = `/api/v1/memory/search${query ? `?${query}` : ""}`;
|
|
432
|
+
const response = await this.http.request(path, {
|
|
433
|
+
method: "GET"
|
|
434
|
+
});
|
|
435
|
+
return response.data;
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Execute batch operations atomically
|
|
439
|
+
*/
|
|
440
|
+
async batch(body) {
|
|
441
|
+
if (!body.operations || body.operations.length === 0) {
|
|
442
|
+
throw new Error("At least one operation is required");
|
|
443
|
+
}
|
|
444
|
+
const apiBody = {
|
|
445
|
+
operations: body.operations.map((op) => ({
|
|
446
|
+
...op,
|
|
447
|
+
operation: op.op,
|
|
448
|
+
op: void 0
|
|
449
|
+
}))
|
|
450
|
+
};
|
|
451
|
+
const response = await this.http.request("/api/v1/memory/batch", {
|
|
452
|
+
method: "POST",
|
|
453
|
+
body: apiBody
|
|
454
|
+
});
|
|
455
|
+
return response.data;
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
var ThreadsNamespace = class {
|
|
459
|
+
constructor(http) {
|
|
460
|
+
this.http = http;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Create a new thread
|
|
464
|
+
*/
|
|
465
|
+
async create(input) {
|
|
466
|
+
const response = await this.http.request("/api/v1/threads", {
|
|
467
|
+
method: "POST",
|
|
468
|
+
body: input || {}
|
|
469
|
+
});
|
|
470
|
+
if (response.data.thread) {
|
|
471
|
+
return response.data.thread;
|
|
472
|
+
} else if (response.data.ok !== void 0) {
|
|
473
|
+
const { ok, ...thread } = response.data;
|
|
474
|
+
return thread;
|
|
475
|
+
} else {
|
|
476
|
+
return response.data;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* List threads with pagination
|
|
481
|
+
*/
|
|
482
|
+
async list(params) {
|
|
483
|
+
const queryParams = new URLSearchParams();
|
|
484
|
+
if (params?.limit) queryParams.append("limit", params.limit.toString());
|
|
485
|
+
if (params?.offset) queryParams.append("offset", params.offset.toString());
|
|
486
|
+
const path = queryParams.toString() ? `/api/v1/threads?${queryParams.toString()}` : "/api/v1/threads";
|
|
487
|
+
const response = await this.http.request(path, {
|
|
488
|
+
method: "GET"
|
|
489
|
+
});
|
|
490
|
+
return response.data;
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Get a thread by ID, optionally with memories
|
|
494
|
+
*/
|
|
495
|
+
async get(id, options) {
|
|
496
|
+
const queryParams = new URLSearchParams();
|
|
497
|
+
if (options?.includeMemories) queryParams.append("include", "memories");
|
|
498
|
+
if (options?.limit) queryParams.append("limit", options.limit.toString());
|
|
499
|
+
if (options?.offset) queryParams.append("offset", options.offset.toString());
|
|
500
|
+
const path = queryParams.toString() ? `/api/v1/threads/${id}?${queryParams.toString()}` : `/api/v1/threads/${id}`;
|
|
501
|
+
const response = await this.http.request(path, {
|
|
502
|
+
method: "GET"
|
|
503
|
+
});
|
|
504
|
+
return response.data;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Update a thread
|
|
508
|
+
*/
|
|
509
|
+
async update(id, input) {
|
|
510
|
+
const response = await this.http.request(`/api/v1/threads/${id}`, {
|
|
511
|
+
method: "PATCH",
|
|
512
|
+
body: input
|
|
513
|
+
});
|
|
514
|
+
const { ok, ...thread } = response.data;
|
|
515
|
+
return thread;
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Delete a thread (memories are unlinked, not deleted)
|
|
519
|
+
*/
|
|
520
|
+
async delete(id) {
|
|
521
|
+
const response = await this.http.request(
|
|
522
|
+
`/api/v1/threads/${id}`,
|
|
523
|
+
{
|
|
524
|
+
method: "DELETE"
|
|
525
|
+
}
|
|
526
|
+
);
|
|
527
|
+
return response.data;
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Link a memory to a thread
|
|
531
|
+
*/
|
|
532
|
+
async linkMemory(threadId, memoryId) {
|
|
533
|
+
const response = await this.http.request(
|
|
534
|
+
`/api/v1/threads/${threadId}/link`,
|
|
535
|
+
{
|
|
536
|
+
method: "POST",
|
|
537
|
+
body: { memory_id: memoryId }
|
|
538
|
+
}
|
|
539
|
+
);
|
|
540
|
+
return response.data;
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
var ChatNamespace = class {
|
|
544
|
+
constructor(http) {
|
|
545
|
+
this.http = http;
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Create a new chat session
|
|
549
|
+
*/
|
|
550
|
+
async createSession(input) {
|
|
551
|
+
const response = await this.http.request("/api/v1/chat/sessions", {
|
|
552
|
+
method: "POST",
|
|
553
|
+
body: input || {}
|
|
554
|
+
});
|
|
555
|
+
return response.data.session;
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Get a chat session by ID
|
|
559
|
+
*/
|
|
560
|
+
async getSession(sessionId) {
|
|
561
|
+
const response = await this.http.request(`/api/v1/chat/sessions/${sessionId}`, {
|
|
562
|
+
method: "GET"
|
|
563
|
+
});
|
|
564
|
+
return response.data;
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* List chat sessions
|
|
568
|
+
*/
|
|
569
|
+
async listSessions(params) {
|
|
570
|
+
const queryParams = new URLSearchParams();
|
|
571
|
+
if (params?.limit) queryParams.append("limit", params.limit.toString());
|
|
572
|
+
if (params?.offset) queryParams.append("offset", params.offset.toString());
|
|
573
|
+
const path = queryParams.toString() ? `/api/v1/chat/sessions?${queryParams.toString()}` : "/api/v1/chat/sessions";
|
|
574
|
+
const response = await this.http.request(path, {
|
|
575
|
+
method: "GET"
|
|
576
|
+
});
|
|
577
|
+
return response.data.sessions;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Get messages for a session
|
|
581
|
+
*/
|
|
582
|
+
async getMessages(sessionId, limit) {
|
|
583
|
+
const queryParams = new URLSearchParams();
|
|
584
|
+
if (limit) queryParams.append("limit", limit.toString());
|
|
585
|
+
const path = queryParams.toString() ? `/api/v1/chat/sessions/${sessionId}/messages?${queryParams.toString()}` : `/api/v1/chat/sessions/${sessionId}/messages`;
|
|
586
|
+
const response = await this.http.request(path, {
|
|
587
|
+
method: "GET"
|
|
588
|
+
});
|
|
589
|
+
return response.data.messages;
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Send a message with SSE streaming
|
|
593
|
+
*/
|
|
594
|
+
async *sendMessage(sessionId, message, opts) {
|
|
595
|
+
const body = {
|
|
596
|
+
message,
|
|
597
|
+
provider: opts?.provider,
|
|
598
|
+
model: opts?.model,
|
|
599
|
+
useMemory: opts?.useMemory ?? true,
|
|
600
|
+
captureToMemory: opts?.captureToMemory ?? true
|
|
601
|
+
};
|
|
602
|
+
const response = await fetch(`${this.http.baseUrl}/api/v1/chat/sessions/${sessionId}/messages`, {
|
|
603
|
+
method: "POST",
|
|
604
|
+
headers: {
|
|
605
|
+
"authorization": `Bearer ${this.http.apiKey}`,
|
|
606
|
+
"content-type": "application/json",
|
|
607
|
+
"accept": "text/event-stream"
|
|
608
|
+
},
|
|
609
|
+
body: JSON.stringify(body),
|
|
610
|
+
signal: opts?.signal
|
|
611
|
+
});
|
|
612
|
+
if (!response.ok) {
|
|
613
|
+
const error = await response.json().catch(() => ({ message: "Request failed" }));
|
|
614
|
+
yield { type: "error", data: error.message || "Request failed" };
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
const reader = response.body?.getReader();
|
|
618
|
+
if (!reader) {
|
|
619
|
+
yield { type: "error", data: "No response body" };
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
const decoder = new TextDecoder();
|
|
623
|
+
let buffer = "";
|
|
624
|
+
try {
|
|
625
|
+
while (true) {
|
|
626
|
+
const { done, value } = await reader.read();
|
|
627
|
+
if (done) break;
|
|
628
|
+
buffer += decoder.decode(value, { stream: true });
|
|
629
|
+
const events = parseSSE(buffer);
|
|
630
|
+
buffer = events.buffer;
|
|
631
|
+
for (const event of events.events) {
|
|
632
|
+
if (event.data === "[DONE]") {
|
|
633
|
+
yield { type: "done" };
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
try {
|
|
637
|
+
const parsed = JSON.parse(event.data);
|
|
638
|
+
if (parsed.content) {
|
|
639
|
+
yield { type: "delta", data: parsed.content };
|
|
640
|
+
}
|
|
641
|
+
} catch {
|
|
642
|
+
yield { type: "delta", data: event.data };
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
} catch (error) {
|
|
647
|
+
if (error.name === "AbortError") {
|
|
648
|
+
yield { type: "error", data: "Request cancelled" };
|
|
649
|
+
} else {
|
|
650
|
+
yield { type: "error", data: error.message };
|
|
651
|
+
}
|
|
652
|
+
} finally {
|
|
653
|
+
reader.releaseLock();
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
var KeysNamespace = class {
|
|
658
|
+
constructor(http) {
|
|
659
|
+
this.http = http;
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* List API keys
|
|
663
|
+
*/
|
|
664
|
+
async list() {
|
|
665
|
+
const response = await this.http.request("/api/v1/keys", {
|
|
666
|
+
method: "GET"
|
|
667
|
+
});
|
|
668
|
+
return response.data.keys;
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Create a new API key
|
|
672
|
+
*/
|
|
673
|
+
async create(name) {
|
|
674
|
+
const response = await this.http.request("/api/v1/keys", {
|
|
675
|
+
method: "POST",
|
|
676
|
+
body: { name }
|
|
677
|
+
});
|
|
678
|
+
return response.data;
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Update an API key
|
|
682
|
+
*/
|
|
683
|
+
async update(id, patch) {
|
|
684
|
+
const response = await this.http.request("/api/v1/keys", {
|
|
685
|
+
method: "PATCH",
|
|
686
|
+
body: { id, ...patch }
|
|
687
|
+
});
|
|
688
|
+
return response.data;
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Revoke an API key
|
|
692
|
+
*/
|
|
693
|
+
async revoke(id) {
|
|
694
|
+
await this.http.request(`/api/v1/keys/${id}`, {
|
|
695
|
+
method: "DELETE"
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
var RecallNamespace = class {
|
|
700
|
+
constructor(http) {
|
|
701
|
+
this.http = http;
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Extract profile entity from text (client-side)
|
|
705
|
+
*/
|
|
706
|
+
extractProfile(text) {
|
|
707
|
+
const s = (text || "").toLowerCase().trim();
|
|
708
|
+
const sanitized = s.replace(/'/g, "'");
|
|
709
|
+
if (/\bmy\s+name\s+is\s+([a-z\-.''\s]{2,}?)(?:[.!?\s]|$)/i.test(sanitized)) {
|
|
710
|
+
const match = sanitized.match(/\bmy\s+name\s+is\s+([a-z\-.''\s]{2,}?)(?:[.!?\s]|$)/i);
|
|
711
|
+
if (match) {
|
|
712
|
+
const name = match[1].trim().replace(/\s+/g, " ").replace(/\.$/, "");
|
|
713
|
+
return { entity: "name", value: name };
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
if (/\b(i am|i'm|im)\s+(\d{1,3})\s*(years old)?/i.test(sanitized)) {
|
|
717
|
+
const match = sanitized.match(/\b(i am|i'm|im)\s+(\d{1,3})/i);
|
|
718
|
+
if (match) {
|
|
719
|
+
return { entity: "age", value: match[2] };
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
if (/\bmy\s+age\s+is\s+(\d{1,3})/i.test(sanitized)) {
|
|
723
|
+
const match = sanitized.match(/\bmy\s+age\s+is\s+(\d{1,3})/i);
|
|
724
|
+
if (match) {
|
|
725
|
+
return { entity: "age", value: match[1] };
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
const locationPatterns = [
|
|
729
|
+
/\bi\s+live\s+in\s+([a-z\-.''\s]{2,}?)(?:[.!?\s]|$)/i,
|
|
730
|
+
/\bi\s+am\s+based\s+in\s+([a-z\-.''\s]{2,}?)(?:[.!?\s]|$)/i,
|
|
731
|
+
/\bi'm\s+from\s+([a-z\-.''\s]{2,}?)(?:[.!?\s]|$)/i,
|
|
732
|
+
/\bi\s+am\s+from\s+([a-z\-.''\s]{2,}?)(?:[.!?\s]|$)/i,
|
|
733
|
+
/\bresiding\s+in\s+([a-z\-.''\s]{2,}?)(?:[.!?\s]|$)/i,
|
|
734
|
+
/\bcurrently\s+in\s+([a-z\-.''\s]{2,}?)(?:[.!?\s]|$)/i,
|
|
735
|
+
/\bmoved\s+to\s+([a-z\-.''\s]{2,}?)(?:[.!?\s]|$)/i,
|
|
736
|
+
/\blocated\s+in\s+([a-z\-.''\s]{2,}?)(?:[.!?\s]|$)/i
|
|
737
|
+
];
|
|
738
|
+
for (const pattern of locationPatterns) {
|
|
739
|
+
if (pattern.test(sanitized)) {
|
|
740
|
+
const match = sanitized.match(pattern);
|
|
741
|
+
if (match) {
|
|
742
|
+
const location = match[1].trim().replace(/\s+/g, " ").replace(/\.$/, "");
|
|
743
|
+
return { entity: "location", value: location };
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
if (/\bi\s+(like|love|prefer)\s+([^.!?]{2,})/i.test(sanitized)) {
|
|
748
|
+
const match = sanitized.match(/\bi\s+(like|love|prefer)\s+([^.!?]{2,})/i);
|
|
749
|
+
if (match) {
|
|
750
|
+
const pref = match[2].trim().replace(/\s+/g, " ").replace(/\.$/, "");
|
|
751
|
+
return { entity: "preference", value: pref };
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
return { entity: null };
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Get profile facts from memories
|
|
758
|
+
*/
|
|
759
|
+
async getProfileFacts() {
|
|
760
|
+
const response = await this.http.request(
|
|
761
|
+
"/api/v1/memory/search?query=user%20name%20age%20location&limit=20",
|
|
762
|
+
{ method: "GET" }
|
|
763
|
+
);
|
|
764
|
+
const facts = {
|
|
765
|
+
preferences: []
|
|
766
|
+
};
|
|
767
|
+
for (const memory of response.data.results) {
|
|
768
|
+
const content = memory.content.toLowerCase();
|
|
769
|
+
if (/^user\s+name\s+is\s+(.+)$/i.test(content)) {
|
|
770
|
+
const match = content.match(/^user\s+name\s+is\s+(.+)$/i);
|
|
771
|
+
if (match && !facts.name) {
|
|
772
|
+
facts.name = match[1].trim();
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
if (/^user\s+age\s+is\s+(\d+)$/i.test(content)) {
|
|
776
|
+
const match = content.match(/^user\s+age\s+is\s+(\d+)$/i);
|
|
777
|
+
if (match && !facts.age) {
|
|
778
|
+
facts.age = match[1];
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
if (/^user\s+location\s+is\s+(.+)$/i.test(content)) {
|
|
782
|
+
const match = content.match(/^user\s+location\s+is\s+(.+)$/i);
|
|
783
|
+
if (match && !facts.location) {
|
|
784
|
+
facts.location = match[1].trim();
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
if (/^user\s+(likes|loves|prefers)\s+(.+)$/i.test(content)) {
|
|
788
|
+
const match = content.match(/^user\s+(likes|loves|prefers)\s+(.+)$/i);
|
|
789
|
+
if (match) {
|
|
790
|
+
const pref = match[2].trim();
|
|
791
|
+
if (!facts.preferences?.includes(pref)) {
|
|
792
|
+
facts.preferences?.push(pref);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
return facts;
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Save a profile fact in canonical form
|
|
801
|
+
*/
|
|
802
|
+
async saveProfileFact(kind, value) {
|
|
803
|
+
let content;
|
|
804
|
+
switch (kind) {
|
|
805
|
+
case "name":
|
|
806
|
+
content = `user name is ${value}`;
|
|
807
|
+
break;
|
|
808
|
+
case "age":
|
|
809
|
+
content = `user age is ${value}`;
|
|
810
|
+
break;
|
|
811
|
+
case "location":
|
|
812
|
+
content = `user location is ${value}`;
|
|
813
|
+
break;
|
|
814
|
+
case "preference":
|
|
815
|
+
content = `user likes ${value}`;
|
|
816
|
+
break;
|
|
817
|
+
default:
|
|
818
|
+
throw new Error(`Unknown profile entity: ${kind}`);
|
|
819
|
+
}
|
|
820
|
+
const response = await this.http.request("/api/v1/memory", {
|
|
821
|
+
method: "POST",
|
|
822
|
+
body: {
|
|
823
|
+
content,
|
|
824
|
+
type: "preference",
|
|
825
|
+
tags: ["profile", kind]
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
const { ok, ...memory } = response.data;
|
|
829
|
+
return memory;
|
|
830
|
+
}
|
|
831
|
+
};
|
|
832
|
+
var index_default = Sovant;
|
|
833
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
834
|
+
0 && (module.exports = {
|
|
835
|
+
NetworkError,
|
|
836
|
+
Sovant,
|
|
837
|
+
SovantError,
|
|
838
|
+
TimeoutError
|
|
839
|
+
});
|