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