@cilow/sdk 0.2.0 → 0.2.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/README.md +412 -199
- package/dist/client.d.mts +224 -0
- package/dist/client.d.ts +224 -0
- package/dist/client.js +509 -0
- package/dist/client.js.map +1 -0
- package/dist/client.mjs +505 -0
- package/dist/client.mjs.map +1 -0
- package/dist/index.d.mts +94 -390
- package/dist/index.d.ts +94 -390
- package/dist/index.js +745 -350
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +732 -318
- package/dist/index.mjs.map +1 -0
- package/dist/providers/langchain.js +821 -0
- package/dist/providers/langchain.js.map +1 -0
- package/dist/providers/langchain.mjs +816 -0
- package/dist/providers/langchain.mjs.map +1 -0
- package/dist/providers/openai.js +737 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/openai.mjs +732 -0
- package/dist/providers/openai.mjs.map +1 -0
- package/dist/providers/vercel.js +866 -0
- package/dist/providers/vercel.js.map +1 -0
- package/dist/providers/vercel.mjs +860 -0
- package/dist/providers/vercel.mjs.map +1 -0
- package/dist/react/hooks.d.mts +327 -0
- package/dist/react/hooks.d.ts +327 -0
- package/dist/react/hooks.js +1183 -0
- package/dist/react/hooks.js.map +1 -0
- package/dist/react/hooks.mjs +1172 -0
- package/dist/react/hooks.mjs.map +1 -0
- package/dist/types.d.mts +494 -0
- package/dist/types.d.ts +494 -0
- package/dist/types.js +18 -0
- package/dist/types.js.map +1 -0
- package/dist/types.mjs +14 -0
- package/dist/types.mjs.map +1 -0
- package/dist/websocket.d.mts +160 -0
- package/dist/websocket.d.ts +160 -0
- package/dist/websocket.js +342 -0
- package/dist/websocket.js.map +1 -0
- package/dist/websocket.mjs +339 -0
- package/dist/websocket.mjs.map +1 -0
- package/package.json +90 -31
- package/LICENSE +0 -21
|
@@ -0,0 +1,1183 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
|
|
6
|
+
// src/react/hooks.tsx
|
|
7
|
+
|
|
8
|
+
// src/client.ts
|
|
9
|
+
async function fetchWithRetry(url, options, config) {
|
|
10
|
+
const controller = new AbortController();
|
|
11
|
+
const timeoutId = setTimeout(() => controller.abort(), config.timeout);
|
|
12
|
+
let lastError = null;
|
|
13
|
+
for (let attempt = 0; attempt <= config.retries; attempt++) {
|
|
14
|
+
try {
|
|
15
|
+
const response = await fetch(url, {
|
|
16
|
+
...options,
|
|
17
|
+
signal: controller.signal
|
|
18
|
+
});
|
|
19
|
+
clearTimeout(timeoutId);
|
|
20
|
+
if (!response.ok) {
|
|
21
|
+
const errorBody = await response.text();
|
|
22
|
+
let apiError;
|
|
23
|
+
try {
|
|
24
|
+
apiError = JSON.parse(errorBody);
|
|
25
|
+
} catch {
|
|
26
|
+
apiError = {
|
|
27
|
+
code: `HTTP_${response.status}`,
|
|
28
|
+
message: errorBody || response.statusText
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
throw new CilowApiError(apiError.message, apiError.code, response.status, apiError.details);
|
|
32
|
+
}
|
|
33
|
+
return response;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
lastError = error;
|
|
36
|
+
if (error instanceof CilowApiError) {
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
if (config.debug) {
|
|
40
|
+
console.warn(`Cilow API request failed (attempt ${attempt + 1}/${config.retries + 1}):`, error);
|
|
41
|
+
}
|
|
42
|
+
if (attempt < config.retries) {
|
|
43
|
+
await new Promise((resolve) => setTimeout(resolve, Math.pow(2, attempt) * 100));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
clearTimeout(timeoutId);
|
|
48
|
+
throw lastError || new Error("Request failed");
|
|
49
|
+
}
|
|
50
|
+
var CilowApiError = class extends Error {
|
|
51
|
+
constructor(message, code, statusCode, details) {
|
|
52
|
+
super(message);
|
|
53
|
+
this.code = code;
|
|
54
|
+
this.statusCode = statusCode;
|
|
55
|
+
this.details = details;
|
|
56
|
+
this.name = "CilowApiError";
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var CilowClient = class {
|
|
60
|
+
constructor(config) {
|
|
61
|
+
this.baseUrl = config.apiUrl.replace(/\/$/, "");
|
|
62
|
+
this.apiKey = config.apiKey;
|
|
63
|
+
this.timeout = config.timeout ?? 3e4;
|
|
64
|
+
this.retries = config.retries ?? 3;
|
|
65
|
+
this.debug = config.debug ?? false;
|
|
66
|
+
const authHeaders = this.apiKey.startsWith("cilow_") ? { "X-API-Key": this.apiKey } : { Authorization: `Bearer ${this.apiKey}` };
|
|
67
|
+
this.headers = {
|
|
68
|
+
"Content-Type": "application/json",
|
|
69
|
+
...authHeaders,
|
|
70
|
+
...config.headers
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Make an API request
|
|
75
|
+
*/
|
|
76
|
+
async request(method, path, body, queryParams) {
|
|
77
|
+
let url = `${this.baseUrl}${path}`;
|
|
78
|
+
if (queryParams) {
|
|
79
|
+
const params = new URLSearchParams();
|
|
80
|
+
for (const [key, value] of Object.entries(queryParams)) {
|
|
81
|
+
if (value !== void 0) {
|
|
82
|
+
params.append(key, String(value));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const queryString = params.toString();
|
|
86
|
+
if (queryString) {
|
|
87
|
+
url += `?${queryString}`;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (this.debug) {
|
|
91
|
+
console.log(`Cilow API: ${method} ${url}`);
|
|
92
|
+
}
|
|
93
|
+
const response = await fetchWithRetry(
|
|
94
|
+
url,
|
|
95
|
+
{
|
|
96
|
+
method,
|
|
97
|
+
headers: this.headers,
|
|
98
|
+
body: body ? JSON.stringify(body) : void 0
|
|
99
|
+
},
|
|
100
|
+
{ timeout: this.timeout, retries: this.retries, debug: this.debug }
|
|
101
|
+
);
|
|
102
|
+
const text = await response.text();
|
|
103
|
+
if (!text) {
|
|
104
|
+
return void 0;
|
|
105
|
+
}
|
|
106
|
+
return JSON.parse(text);
|
|
107
|
+
}
|
|
108
|
+
// ===========================================================================
|
|
109
|
+
// Simple API (remember, recall, forget)
|
|
110
|
+
// ===========================================================================
|
|
111
|
+
/**
|
|
112
|
+
* Store a memory (simple API)
|
|
113
|
+
*
|
|
114
|
+
* @param content - The content to remember
|
|
115
|
+
* @param options - Optional configuration
|
|
116
|
+
* @returns The created memory ID
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```typescript
|
|
120
|
+
* await cilow.remember("User prefers dark mode", {
|
|
121
|
+
* tags: ["preference"],
|
|
122
|
+
* userId: "user-123"
|
|
123
|
+
* });
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
async remember(content, options) {
|
|
127
|
+
const response = await this.request("POST", "/api/v1/memory/add", {
|
|
128
|
+
content,
|
|
129
|
+
tags: options?.tags ?? [],
|
|
130
|
+
user_id: options?.userId,
|
|
131
|
+
session_id: options?.sessionId,
|
|
132
|
+
metadata: options?.metadata,
|
|
133
|
+
type: options?.tier
|
|
134
|
+
});
|
|
135
|
+
return response.memory_id || response.id || "";
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Search memories (simple API)
|
|
139
|
+
*
|
|
140
|
+
* @param query - Search query text
|
|
141
|
+
* @param options - Search options
|
|
142
|
+
* @returns Array of search results
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* const memories = await cilow.recall("user preferences", {
|
|
147
|
+
* limit: 10,
|
|
148
|
+
* tags: ["preference"]
|
|
149
|
+
* });
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
async recall(query, options) {
|
|
153
|
+
const response = await this.request("POST", "/api/v1/memory/search", {
|
|
154
|
+
query,
|
|
155
|
+
limit: options?.limit ?? 10,
|
|
156
|
+
min_score: options?.minRelevance ?? 0.3,
|
|
157
|
+
tags: options?.tags,
|
|
158
|
+
user_id: options?.userId
|
|
159
|
+
});
|
|
160
|
+
return (response.results ?? []).map((r) => ({
|
|
161
|
+
memory: {
|
|
162
|
+
id: r.memory_id,
|
|
163
|
+
content: r.content,
|
|
164
|
+
tags: r.tags ?? [],
|
|
165
|
+
tier: "hot",
|
|
166
|
+
status: "active",
|
|
167
|
+
accessCount: 0,
|
|
168
|
+
tokenCount: Math.ceil(r.content.length / 4),
|
|
169
|
+
metadata: {},
|
|
170
|
+
createdAt: r.created_at ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
171
|
+
updatedAt: r.created_at ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
172
|
+
lastAccessedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
173
|
+
},
|
|
174
|
+
score: r.similarity,
|
|
175
|
+
highlights: []
|
|
176
|
+
}));
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Delete memories (simple API)
|
|
180
|
+
*
|
|
181
|
+
* @param filter - Filter criteria for deletion
|
|
182
|
+
* @returns Number of memories deleted
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```typescript
|
|
186
|
+
* // Delete by tags
|
|
187
|
+
* await cilow.forget({ tags: ["temporary"] });
|
|
188
|
+
*
|
|
189
|
+
* // Delete by user
|
|
190
|
+
* await cilow.forget({ userId: "user-123" });
|
|
191
|
+
*
|
|
192
|
+
* // Delete specific memory
|
|
193
|
+
* await cilow.forget({ memoryId: "mem-abc" });
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
async forget(filter) {
|
|
197
|
+
if (filter.memoryId) {
|
|
198
|
+
await this.deleteMemory(filter.memoryId);
|
|
199
|
+
return 1;
|
|
200
|
+
}
|
|
201
|
+
const response = await this.request("DELETE", "/api/v1/memories/bulk", {
|
|
202
|
+
filter_tags: filter.tags,
|
|
203
|
+
user_id: filter.userId,
|
|
204
|
+
session_id: filter.sessionId,
|
|
205
|
+
older_than: filter.olderThan
|
|
206
|
+
});
|
|
207
|
+
return response.deleted ?? 0;
|
|
208
|
+
}
|
|
209
|
+
// ===========================================================================
|
|
210
|
+
// Memory CRUD Operations
|
|
211
|
+
// ===========================================================================
|
|
212
|
+
/**
|
|
213
|
+
* Create a new memory
|
|
214
|
+
*/
|
|
215
|
+
async createMemory(content, options) {
|
|
216
|
+
return this.request("POST", "/api/v1/memories", {
|
|
217
|
+
content,
|
|
218
|
+
tags: options?.tags ?? [],
|
|
219
|
+
user_id: options?.userId,
|
|
220
|
+
session_id: options?.sessionId,
|
|
221
|
+
metadata: options?.metadata,
|
|
222
|
+
tier: options?.tier
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Get a memory by ID
|
|
227
|
+
*/
|
|
228
|
+
async getMemory(memoryId) {
|
|
229
|
+
return this.request("GET", `/api/v1/memories/${memoryId}`);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Update a memory
|
|
233
|
+
*/
|
|
234
|
+
async updateMemory(memoryId, updates) {
|
|
235
|
+
return this.request("PATCH", `/api/v1/memories/${memoryId}`, {
|
|
236
|
+
content: updates.content,
|
|
237
|
+
tags: updates.tags,
|
|
238
|
+
metadata: updates.metadata,
|
|
239
|
+
tier: updates.tier
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Delete a memory
|
|
244
|
+
*/
|
|
245
|
+
async deleteMemory(memoryId) {
|
|
246
|
+
await this.request("DELETE", `/api/v1/memories/${memoryId}`);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* List memories with pagination
|
|
250
|
+
*/
|
|
251
|
+
async listMemories(options) {
|
|
252
|
+
const response = await this.request(
|
|
253
|
+
"GET",
|
|
254
|
+
"/api/v1/memory/list",
|
|
255
|
+
void 0,
|
|
256
|
+
{
|
|
257
|
+
limit: options?.limit ?? 20,
|
|
258
|
+
offset: options?.offset ?? 0,
|
|
259
|
+
user_id: options?.userId,
|
|
260
|
+
session_id: options?.sessionId,
|
|
261
|
+
tags: options?.tags?.join(","),
|
|
262
|
+
type: options?.tier
|
|
263
|
+
}
|
|
264
|
+
);
|
|
265
|
+
const items = (response.memories ?? []).map((m) => ({
|
|
266
|
+
id: m.memory_id || m.id || "",
|
|
267
|
+
content: m.content,
|
|
268
|
+
tags: m.tags ?? [],
|
|
269
|
+
createdAt: m.created_at ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
270
|
+
tier: m.type || "hot"
|
|
271
|
+
}));
|
|
272
|
+
const limit = options?.limit ?? 20;
|
|
273
|
+
const offset = options?.offset ?? 0;
|
|
274
|
+
const total = response.total ?? items.length;
|
|
275
|
+
return {
|
|
276
|
+
items,
|
|
277
|
+
total,
|
|
278
|
+
limit,
|
|
279
|
+
offset,
|
|
280
|
+
hasMore: offset + items.length < total
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
// ===========================================================================
|
|
284
|
+
// Search Operations
|
|
285
|
+
// ===========================================================================
|
|
286
|
+
/**
|
|
287
|
+
* Search memories with full options
|
|
288
|
+
*/
|
|
289
|
+
async searchMemories(query) {
|
|
290
|
+
const response = await this.request("POST", "/api/v1/memories/search", {
|
|
291
|
+
text: query.text,
|
|
292
|
+
limit: query.limit ?? 10,
|
|
293
|
+
min_relevance: query.minRelevance ?? 0.3,
|
|
294
|
+
filter_tags: query.tags,
|
|
295
|
+
user_id: query.userId,
|
|
296
|
+
session_id: query.sessionId,
|
|
297
|
+
tier: query.tier,
|
|
298
|
+
created_after: query.createdAfter,
|
|
299
|
+
created_before: query.createdBefore,
|
|
300
|
+
include_archived: query.includeArchived,
|
|
301
|
+
mode: query.mode ?? "hybrid"
|
|
302
|
+
});
|
|
303
|
+
return response.results ?? [];
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Advanced search with reranking and boosts
|
|
307
|
+
*/
|
|
308
|
+
async advancedSearch(query) {
|
|
309
|
+
const response = await this.request("POST", "/api/v1/memories/search/advanced", {
|
|
310
|
+
text: query.text,
|
|
311
|
+
limit: query.limit ?? 10,
|
|
312
|
+
min_relevance: query.minRelevance ?? 0.3,
|
|
313
|
+
filter_tags: query.tags,
|
|
314
|
+
required_tags: query.requiredTags,
|
|
315
|
+
excluded_tags: query.excludedTags,
|
|
316
|
+
user_id: query.userId,
|
|
317
|
+
session_id: query.sessionId,
|
|
318
|
+
recency_boost: query.recencyBoost,
|
|
319
|
+
frequency_boost: query.frequencyBoost,
|
|
320
|
+
metadata_filters: query.metadataFilters,
|
|
321
|
+
reranker: query.reranker
|
|
322
|
+
});
|
|
323
|
+
return response.results ?? [];
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Get context for AI applications
|
|
327
|
+
*/
|
|
328
|
+
async getContext(query, options) {
|
|
329
|
+
const maxTokens = options?.maxTokens ?? 4e3;
|
|
330
|
+
const tokensPerChar = 0.25;
|
|
331
|
+
const results = await this.searchMemories({
|
|
332
|
+
text: query,
|
|
333
|
+
limit: 30,
|
|
334
|
+
minRelevance: 0.3,
|
|
335
|
+
userId: options?.userId,
|
|
336
|
+
tags: options?.tags
|
|
337
|
+
});
|
|
338
|
+
let context = "";
|
|
339
|
+
let estimatedTokens = 0;
|
|
340
|
+
const memoryIds = [];
|
|
341
|
+
const scores = [];
|
|
342
|
+
for (const result of results) {
|
|
343
|
+
const memoryText = `[Relevance: ${(result.score * 100).toFixed(0)}%]
|
|
344
|
+
${result.memory.content}
|
|
345
|
+
|
|
346
|
+
`;
|
|
347
|
+
const memoryTokens = Math.ceil(memoryText.length * tokensPerChar);
|
|
348
|
+
if (estimatedTokens + memoryTokens > maxTokens) break;
|
|
349
|
+
context += memoryText;
|
|
350
|
+
estimatedTokens += memoryTokens;
|
|
351
|
+
memoryIds.push(result.memory.id);
|
|
352
|
+
scores.push(result.score);
|
|
353
|
+
}
|
|
354
|
+
return {
|
|
355
|
+
context: context.trim(),
|
|
356
|
+
memoriesUsed: memoryIds.length,
|
|
357
|
+
estimatedTokens,
|
|
358
|
+
memoryIds,
|
|
359
|
+
scores
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
// ===========================================================================
|
|
363
|
+
// Conversation Operations
|
|
364
|
+
// ===========================================================================
|
|
365
|
+
/**
|
|
366
|
+
* Store a conversation turn
|
|
367
|
+
*/
|
|
368
|
+
async storeConversation(turn) {
|
|
369
|
+
const content = `User: ${turn.userMessage}
|
|
370
|
+
|
|
371
|
+
Assistant: ${turn.assistantResponse}`;
|
|
372
|
+
const tags = ["conversation"];
|
|
373
|
+
if (turn.sessionId) {
|
|
374
|
+
tags.push(`session:${turn.sessionId}`);
|
|
375
|
+
}
|
|
376
|
+
return this.remember(content, {
|
|
377
|
+
tags,
|
|
378
|
+
userId: turn.userId,
|
|
379
|
+
sessionId: turn.sessionId,
|
|
380
|
+
metadata: {
|
|
381
|
+
type: "conversation",
|
|
382
|
+
...turn.metadata
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Get conversation history for a session
|
|
388
|
+
*/
|
|
389
|
+
async getConversationHistory(sessionId, options) {
|
|
390
|
+
const response = await this.listMemories({
|
|
391
|
+
sessionId,
|
|
392
|
+
tags: ["conversation"],
|
|
393
|
+
limit: options?.limit ?? 50,
|
|
394
|
+
offset: options?.offset ?? 0
|
|
395
|
+
});
|
|
396
|
+
return response.items;
|
|
397
|
+
}
|
|
398
|
+
// ===========================================================================
|
|
399
|
+
// Graph Operations
|
|
400
|
+
// ===========================================================================
|
|
401
|
+
/**
|
|
402
|
+
* Get a graph node
|
|
403
|
+
*/
|
|
404
|
+
async getGraphNode(nodeId) {
|
|
405
|
+
return this.request("GET", `/api/v1/graph/nodes/${nodeId}`);
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Create a graph node
|
|
409
|
+
*/
|
|
410
|
+
async createGraphNode(type, name, properties) {
|
|
411
|
+
return this.request("POST", "/api/v1/graph/nodes", {
|
|
412
|
+
type,
|
|
413
|
+
name,
|
|
414
|
+
properties: properties ?? {}
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Create a graph edge
|
|
419
|
+
*/
|
|
420
|
+
async createGraphEdge(sourceId, targetId, type, properties) {
|
|
421
|
+
return this.request("POST", "/api/v1/graph/edges", {
|
|
422
|
+
source_id: sourceId,
|
|
423
|
+
target_id: targetId,
|
|
424
|
+
type,
|
|
425
|
+
properties: properties ?? {}
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Traverse the graph from a starting node
|
|
430
|
+
*/
|
|
431
|
+
async traverseGraph(options) {
|
|
432
|
+
return this.request("POST", "/api/v1/graph/traverse", {
|
|
433
|
+
start_node_id: options.startNodeId,
|
|
434
|
+
max_depth: options.maxDepth ?? 3,
|
|
435
|
+
relationship_types: options.relationshipTypes,
|
|
436
|
+
limit: options.limit ?? 100,
|
|
437
|
+
direction: options.direction ?? "both"
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Get related nodes for a memory
|
|
442
|
+
*/
|
|
443
|
+
async getRelatedNodes(memoryId) {
|
|
444
|
+
const response = await this.request(
|
|
445
|
+
"GET",
|
|
446
|
+
`/api/v1/memories/${memoryId}/related-nodes`
|
|
447
|
+
);
|
|
448
|
+
return response.nodes ?? [];
|
|
449
|
+
}
|
|
450
|
+
// ===========================================================================
|
|
451
|
+
// Statistics & Admin
|
|
452
|
+
// ===========================================================================
|
|
453
|
+
/**
|
|
454
|
+
* Get memory statistics
|
|
455
|
+
*/
|
|
456
|
+
async getStats() {
|
|
457
|
+
return this.request("GET", "/api/v1/stats");
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Get all tags with counts
|
|
461
|
+
*/
|
|
462
|
+
async getTags() {
|
|
463
|
+
const response = await this.request("GET", "/api/v1/tags");
|
|
464
|
+
return response.tags ?? [];
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Get user statistics
|
|
468
|
+
*/
|
|
469
|
+
async getUserStats(userId) {
|
|
470
|
+
return this.request("GET", `/api/v1/users/${userId}/stats`);
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Health check
|
|
474
|
+
*/
|
|
475
|
+
async healthCheck() {
|
|
476
|
+
return this.request("GET", "/health");
|
|
477
|
+
}
|
|
478
|
+
// ===========================================================================
|
|
479
|
+
// Batch Operations
|
|
480
|
+
// ===========================================================================
|
|
481
|
+
/**
|
|
482
|
+
* Batch create memories
|
|
483
|
+
*/
|
|
484
|
+
async batchCreate(items) {
|
|
485
|
+
const response = await this.request("POST", "/api/v1/memories/batch", {
|
|
486
|
+
memories: items.map((item) => ({
|
|
487
|
+
content: item.content,
|
|
488
|
+
tags: item.options?.tags ?? [],
|
|
489
|
+
user_id: item.options?.userId,
|
|
490
|
+
session_id: item.options?.sessionId,
|
|
491
|
+
metadata: item.options?.metadata
|
|
492
|
+
}))
|
|
493
|
+
});
|
|
494
|
+
return response.ids ?? [];
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Batch delete memories
|
|
498
|
+
*/
|
|
499
|
+
async batchDelete(memoryIds) {
|
|
500
|
+
const response = await this.request("DELETE", "/api/v1/memories/batch", {
|
|
501
|
+
ids: memoryIds
|
|
502
|
+
});
|
|
503
|
+
return response.deleted ?? 0;
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
// src/websocket.ts
|
|
508
|
+
var CilowWebSocket = class {
|
|
509
|
+
constructor(config) {
|
|
510
|
+
this.ws = null;
|
|
511
|
+
this.state = "disconnected";
|
|
512
|
+
this.reconnectCount = 0;
|
|
513
|
+
this.heartbeatTimer = null;
|
|
514
|
+
this.reconnectTimer = null;
|
|
515
|
+
this.messageQueue = [];
|
|
516
|
+
this.subscriptions = [];
|
|
517
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
518
|
+
this.allListeners = /* @__PURE__ */ new Set();
|
|
519
|
+
const httpUrl = config.apiUrl.replace(/\/$/, "");
|
|
520
|
+
this.wsUrl = config.wsUrl ?? httpUrl.replace(/^http/, "ws") + "/ws";
|
|
521
|
+
this.apiKey = config.apiKey;
|
|
522
|
+
this.reconnectAttempts = config.reconnectAttempts ?? 5;
|
|
523
|
+
this.reconnectDelay = config.reconnectDelay ?? 1e3;
|
|
524
|
+
this.heartbeatInterval = config.heartbeatInterval ?? 3e4;
|
|
525
|
+
this.messageQueueSize = config.messageQueueSize ?? 100;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Get current connection state
|
|
529
|
+
*/
|
|
530
|
+
get connectionState() {
|
|
531
|
+
return this.state;
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Check if connected
|
|
535
|
+
*/
|
|
536
|
+
get isConnected() {
|
|
537
|
+
return this.state === "connected";
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Connect to the WebSocket server
|
|
541
|
+
*/
|
|
542
|
+
async connect() {
|
|
543
|
+
if (this.state === "connected" || this.state === "connecting") {
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
this.state = "connecting";
|
|
547
|
+
this.emitStatusEvent("connecting");
|
|
548
|
+
return new Promise((resolve, reject) => {
|
|
549
|
+
try {
|
|
550
|
+
const url = new URL(this.wsUrl);
|
|
551
|
+
url.searchParams.set("token", this.apiKey);
|
|
552
|
+
this.ws = new WebSocket(url.toString());
|
|
553
|
+
this.ws.onopen = () => {
|
|
554
|
+
this.state = "connected";
|
|
555
|
+
this.reconnectCount = 0;
|
|
556
|
+
this.emitStatusEvent("connected");
|
|
557
|
+
this.startHeartbeat();
|
|
558
|
+
this.flushMessageQueue();
|
|
559
|
+
this.resubscribe();
|
|
560
|
+
resolve();
|
|
561
|
+
};
|
|
562
|
+
this.ws.onclose = (event) => {
|
|
563
|
+
this.handleClose(event);
|
|
564
|
+
};
|
|
565
|
+
this.ws.onerror = (error) => {
|
|
566
|
+
if (this.state === "connecting") {
|
|
567
|
+
reject(new Error("WebSocket connection failed"));
|
|
568
|
+
}
|
|
569
|
+
this.handleError(error);
|
|
570
|
+
};
|
|
571
|
+
this.ws.onmessage = (event) => {
|
|
572
|
+
this.handleMessage(event);
|
|
573
|
+
};
|
|
574
|
+
} catch (error) {
|
|
575
|
+
this.state = "disconnected";
|
|
576
|
+
reject(error);
|
|
577
|
+
}
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Disconnect from the WebSocket server
|
|
582
|
+
*/
|
|
583
|
+
disconnect() {
|
|
584
|
+
this.stopHeartbeat();
|
|
585
|
+
this.clearReconnectTimer();
|
|
586
|
+
if (this.ws) {
|
|
587
|
+
this.ws.onclose = null;
|
|
588
|
+
this.ws.close(1e3, "Client disconnect");
|
|
589
|
+
this.ws = null;
|
|
590
|
+
}
|
|
591
|
+
this.state = "disconnected";
|
|
592
|
+
this.emitStatusEvent("disconnected", "Client initiated disconnect");
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Subscribe to events with optional filter
|
|
596
|
+
*/
|
|
597
|
+
subscribe(filter) {
|
|
598
|
+
if (filter) {
|
|
599
|
+
this.subscriptions.push(filter);
|
|
600
|
+
}
|
|
601
|
+
if (this.isConnected) {
|
|
602
|
+
this.sendSubscription(filter);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Unsubscribe from events
|
|
607
|
+
*/
|
|
608
|
+
unsubscribe(filter) {
|
|
609
|
+
if (filter) {
|
|
610
|
+
this.subscriptions = this.subscriptions.filter(
|
|
611
|
+
(s) => s.userId !== filter.userId || s.sessionId !== filter.sessionId || JSON.stringify(s.tags) !== JSON.stringify(filter.tags)
|
|
612
|
+
);
|
|
613
|
+
} else {
|
|
614
|
+
this.subscriptions = [];
|
|
615
|
+
}
|
|
616
|
+
if (this.isConnected) {
|
|
617
|
+
this.send({
|
|
618
|
+
type: "unsubscribe",
|
|
619
|
+
filter
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Add event listener for specific event type
|
|
625
|
+
*/
|
|
626
|
+
on(eventType, listener) {
|
|
627
|
+
if (!this.listeners.has(eventType)) {
|
|
628
|
+
this.listeners.set(eventType, /* @__PURE__ */ new Set());
|
|
629
|
+
}
|
|
630
|
+
this.listeners.get(eventType).add(listener);
|
|
631
|
+
return () => {
|
|
632
|
+
this.listeners.get(eventType)?.delete(listener);
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Add listener for all events
|
|
637
|
+
*/
|
|
638
|
+
onAny(listener) {
|
|
639
|
+
this.allListeners.add(listener);
|
|
640
|
+
return () => {
|
|
641
|
+
this.allListeners.delete(listener);
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Remove event listener
|
|
646
|
+
*/
|
|
647
|
+
off(eventType, listener) {
|
|
648
|
+
this.listeners.get(eventType)?.delete(listener);
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Remove all listeners for an event type
|
|
652
|
+
*/
|
|
653
|
+
offAll(eventType) {
|
|
654
|
+
if (eventType) {
|
|
655
|
+
this.listeners.delete(eventType);
|
|
656
|
+
} else {
|
|
657
|
+
this.listeners.clear();
|
|
658
|
+
this.allListeners.clear();
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Listen for memory created events
|
|
663
|
+
*/
|
|
664
|
+
onMemoryCreated(listener) {
|
|
665
|
+
return this.on("memory.created", listener);
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Listen for memory updated events
|
|
669
|
+
*/
|
|
670
|
+
onMemoryUpdated(listener) {
|
|
671
|
+
return this.on("memory.updated", listener);
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Listen for memory deleted events
|
|
675
|
+
*/
|
|
676
|
+
onMemoryDeleted(listener) {
|
|
677
|
+
return this.on("memory.deleted", listener);
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Listen for memory tier changed events
|
|
681
|
+
*/
|
|
682
|
+
onMemoryTierChanged(listener) {
|
|
683
|
+
return this.on("memory.tier_changed", listener);
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Listen for graph node created events
|
|
687
|
+
*/
|
|
688
|
+
onGraphNodeCreated(listener) {
|
|
689
|
+
return this.on("graph.node_created", listener);
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Listen for graph edge created events
|
|
693
|
+
*/
|
|
694
|
+
onGraphEdgeCreated(listener) {
|
|
695
|
+
return this.on("graph.edge_created", listener);
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Listen for connection status changes
|
|
699
|
+
*/
|
|
700
|
+
onConnectionStatus(listener) {
|
|
701
|
+
return this.on("connection.status", listener);
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Wait for specific event (one-time)
|
|
705
|
+
*/
|
|
706
|
+
once(eventType, timeout) {
|
|
707
|
+
return new Promise((resolve, reject) => {
|
|
708
|
+
const timeoutId = timeout ? setTimeout(() => {
|
|
709
|
+
unsubscribe();
|
|
710
|
+
reject(new Error(`Timeout waiting for event: ${eventType}`));
|
|
711
|
+
}, timeout) : null;
|
|
712
|
+
const unsubscribe = this.on(eventType, (event) => {
|
|
713
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
714
|
+
unsubscribe();
|
|
715
|
+
resolve(event);
|
|
716
|
+
});
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
// ===========================================================================
|
|
720
|
+
// Private Methods
|
|
721
|
+
// ===========================================================================
|
|
722
|
+
send(message) {
|
|
723
|
+
const data = JSON.stringify(message);
|
|
724
|
+
if (this.isConnected && this.ws) {
|
|
725
|
+
this.ws.send(data);
|
|
726
|
+
} else {
|
|
727
|
+
if (this.messageQueue.length >= this.messageQueueSize) {
|
|
728
|
+
this.messageQueue.shift();
|
|
729
|
+
}
|
|
730
|
+
this.messageQueue.push(data);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
sendSubscription(filter) {
|
|
734
|
+
this.send({
|
|
735
|
+
type: "subscribe",
|
|
736
|
+
filter: filter ?? {}
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
resubscribe() {
|
|
740
|
+
for (const filter of this.subscriptions) {
|
|
741
|
+
this.sendSubscription(filter);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
flushMessageQueue() {
|
|
745
|
+
while (this.messageQueue.length > 0 && this.isConnected && this.ws) {
|
|
746
|
+
const message = this.messageQueue.shift();
|
|
747
|
+
if (message) {
|
|
748
|
+
this.ws.send(message);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
handleMessage(event) {
|
|
753
|
+
try {
|
|
754
|
+
const data = JSON.parse(event.data);
|
|
755
|
+
this.emit(data);
|
|
756
|
+
} catch (error) {
|
|
757
|
+
console.error("Failed to parse WebSocket message:", error);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
handleClose(event) {
|
|
761
|
+
this.stopHeartbeat();
|
|
762
|
+
if (event.code === 1e3) {
|
|
763
|
+
this.state = "disconnected";
|
|
764
|
+
this.emitStatusEvent("disconnected", "Connection closed normally");
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
if (this.reconnectCount < this.reconnectAttempts) {
|
|
768
|
+
this.state = "reconnecting";
|
|
769
|
+
this.emitStatusEvent("reconnecting", `Reconnecting (attempt ${this.reconnectCount + 1}/${this.reconnectAttempts})`);
|
|
770
|
+
this.scheduleReconnect();
|
|
771
|
+
} else {
|
|
772
|
+
this.state = "disconnected";
|
|
773
|
+
this.emitStatusEvent("disconnected", "Max reconnection attempts reached");
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
handleError(_error) {
|
|
777
|
+
console.error("WebSocket error occurred");
|
|
778
|
+
}
|
|
779
|
+
scheduleReconnect() {
|
|
780
|
+
this.clearReconnectTimer();
|
|
781
|
+
const delay = this.reconnectDelay * Math.pow(2, this.reconnectCount);
|
|
782
|
+
this.reconnectCount++;
|
|
783
|
+
this.reconnectTimer = setTimeout(async () => {
|
|
784
|
+
try {
|
|
785
|
+
await this.connect();
|
|
786
|
+
} catch {
|
|
787
|
+
}
|
|
788
|
+
}, delay);
|
|
789
|
+
}
|
|
790
|
+
clearReconnectTimer() {
|
|
791
|
+
if (this.reconnectTimer) {
|
|
792
|
+
clearTimeout(this.reconnectTimer);
|
|
793
|
+
this.reconnectTimer = null;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
startHeartbeat() {
|
|
797
|
+
this.stopHeartbeat();
|
|
798
|
+
this.heartbeatTimer = setInterval(() => {
|
|
799
|
+
if (this.isConnected) {
|
|
800
|
+
this.send({ type: "ping" });
|
|
801
|
+
}
|
|
802
|
+
}, this.heartbeatInterval);
|
|
803
|
+
}
|
|
804
|
+
stopHeartbeat() {
|
|
805
|
+
if (this.heartbeatTimer) {
|
|
806
|
+
clearInterval(this.heartbeatTimer);
|
|
807
|
+
this.heartbeatTimer = null;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
emit(event) {
|
|
811
|
+
const typeListeners = this.listeners.get(event.type);
|
|
812
|
+
if (typeListeners) {
|
|
813
|
+
for (const listener of typeListeners) {
|
|
814
|
+
try {
|
|
815
|
+
listener(event);
|
|
816
|
+
} catch (error) {
|
|
817
|
+
console.error("Error in event listener:", error);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
for (const listener of this.allListeners) {
|
|
822
|
+
try {
|
|
823
|
+
listener(event);
|
|
824
|
+
} catch (error) {
|
|
825
|
+
console.error("Error in event listener:", error);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
emitStatusEvent(status, reason) {
|
|
830
|
+
const event = {
|
|
831
|
+
type: "connection.status",
|
|
832
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
833
|
+
status: status === "connecting" ? "reconnecting" : status,
|
|
834
|
+
reason
|
|
835
|
+
};
|
|
836
|
+
this.emit(event);
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
var CilowContext = react.createContext(null);
|
|
840
|
+
function CilowProvider({
|
|
841
|
+
children,
|
|
842
|
+
userId: initialUserId,
|
|
843
|
+
sessionId: initialSessionId,
|
|
844
|
+
enableWebSocket = false,
|
|
845
|
+
...config
|
|
846
|
+
}) {
|
|
847
|
+
const [userId, setUserId] = react.useState(initialUserId);
|
|
848
|
+
const [sessionId, setSessionId] = react.useState(initialSessionId);
|
|
849
|
+
const [isConnected, setIsConnected] = react.useState(false);
|
|
850
|
+
const client = react.useMemo(() => new CilowClient(config), [config.apiUrl, config.apiKey]);
|
|
851
|
+
const ws = react.useMemo(() => {
|
|
852
|
+
if (!enableWebSocket) return null;
|
|
853
|
+
return new CilowWebSocket(config);
|
|
854
|
+
}, [enableWebSocket, config.apiUrl, config.apiKey]);
|
|
855
|
+
react.useEffect(() => {
|
|
856
|
+
if (!ws) return;
|
|
857
|
+
const connectWs = async () => {
|
|
858
|
+
try {
|
|
859
|
+
await ws.connect();
|
|
860
|
+
setIsConnected(true);
|
|
861
|
+
} catch (error) {
|
|
862
|
+
console.error("Failed to connect WebSocket:", error);
|
|
863
|
+
}
|
|
864
|
+
};
|
|
865
|
+
connectWs();
|
|
866
|
+
const unsubscribe = ws.onConnectionStatus((event) => {
|
|
867
|
+
setIsConnected(event.status === "connected");
|
|
868
|
+
});
|
|
869
|
+
return () => {
|
|
870
|
+
unsubscribe();
|
|
871
|
+
ws.disconnect();
|
|
872
|
+
};
|
|
873
|
+
}, [ws]);
|
|
874
|
+
react.useEffect(() => {
|
|
875
|
+
if (!ws || !userId) return;
|
|
876
|
+
ws.subscribe({ userId });
|
|
877
|
+
}, [ws, userId]);
|
|
878
|
+
const value = react.useMemo(
|
|
879
|
+
() => ({
|
|
880
|
+
client,
|
|
881
|
+
ws,
|
|
882
|
+
userId,
|
|
883
|
+
sessionId,
|
|
884
|
+
setUserId,
|
|
885
|
+
setSessionId,
|
|
886
|
+
isConnected
|
|
887
|
+
}),
|
|
888
|
+
[client, ws, userId, sessionId, isConnected]
|
|
889
|
+
);
|
|
890
|
+
return /* @__PURE__ */ jsxRuntime.jsx(CilowContext.Provider, { value, children });
|
|
891
|
+
}
|
|
892
|
+
function useCilow() {
|
|
893
|
+
const context = react.useContext(CilowContext);
|
|
894
|
+
if (!context) {
|
|
895
|
+
throw new Error("useCilow must be used within a CilowProvider");
|
|
896
|
+
}
|
|
897
|
+
return context;
|
|
898
|
+
}
|
|
899
|
+
function useMemory() {
|
|
900
|
+
const { client, userId, sessionId } = useCilow();
|
|
901
|
+
const [isLoading, setIsLoading] = react.useState(false);
|
|
902
|
+
const [error, setError] = react.useState(null);
|
|
903
|
+
const remember = react.useCallback(
|
|
904
|
+
async (content, options) => {
|
|
905
|
+
setIsLoading(true);
|
|
906
|
+
setError(null);
|
|
907
|
+
try {
|
|
908
|
+
const memoryId = await client.remember(content, {
|
|
909
|
+
...options,
|
|
910
|
+
userId: options?.userId ?? userId,
|
|
911
|
+
sessionId: options?.sessionId ?? sessionId
|
|
912
|
+
});
|
|
913
|
+
return memoryId;
|
|
914
|
+
} catch (err) {
|
|
915
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
916
|
+
setError(error2);
|
|
917
|
+
throw error2;
|
|
918
|
+
} finally {
|
|
919
|
+
setIsLoading(false);
|
|
920
|
+
}
|
|
921
|
+
},
|
|
922
|
+
[client, userId, sessionId]
|
|
923
|
+
);
|
|
924
|
+
const forget = react.useCallback(
|
|
925
|
+
async (filter) => {
|
|
926
|
+
setIsLoading(true);
|
|
927
|
+
setError(null);
|
|
928
|
+
try {
|
|
929
|
+
const count = await client.forget({
|
|
930
|
+
...filter,
|
|
931
|
+
userId
|
|
932
|
+
});
|
|
933
|
+
return count;
|
|
934
|
+
} catch (err) {
|
|
935
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
936
|
+
setError(error2);
|
|
937
|
+
throw error2;
|
|
938
|
+
} finally {
|
|
939
|
+
setIsLoading(false);
|
|
940
|
+
}
|
|
941
|
+
},
|
|
942
|
+
[client, userId]
|
|
943
|
+
);
|
|
944
|
+
const getMemory = react.useCallback(
|
|
945
|
+
async (memoryId) => {
|
|
946
|
+
setIsLoading(true);
|
|
947
|
+
setError(null);
|
|
948
|
+
try {
|
|
949
|
+
return await client.getMemory(memoryId);
|
|
950
|
+
} catch (err) {
|
|
951
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
952
|
+
setError(error2);
|
|
953
|
+
throw error2;
|
|
954
|
+
} finally {
|
|
955
|
+
setIsLoading(false);
|
|
956
|
+
}
|
|
957
|
+
},
|
|
958
|
+
[client]
|
|
959
|
+
);
|
|
960
|
+
return { remember, forget, getMemory, isLoading, error };
|
|
961
|
+
}
|
|
962
|
+
function useRecall() {
|
|
963
|
+
const { client, userId } = useCilow();
|
|
964
|
+
const [data, setData] = react.useState([]);
|
|
965
|
+
const [isLoading, setIsLoading] = react.useState(false);
|
|
966
|
+
const [error, setError] = react.useState(null);
|
|
967
|
+
const search = react.useCallback(
|
|
968
|
+
async (query, options) => {
|
|
969
|
+
setIsLoading(true);
|
|
970
|
+
setError(null);
|
|
971
|
+
try {
|
|
972
|
+
const results = await client.recall(query, {
|
|
973
|
+
...options,
|
|
974
|
+
userId
|
|
975
|
+
});
|
|
976
|
+
setData(results);
|
|
977
|
+
} catch (err) {
|
|
978
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
979
|
+
setError(error2);
|
|
980
|
+
setData([]);
|
|
981
|
+
} finally {
|
|
982
|
+
setIsLoading(false);
|
|
983
|
+
}
|
|
984
|
+
},
|
|
985
|
+
[client, userId]
|
|
986
|
+
);
|
|
987
|
+
const clear = react.useCallback(() => {
|
|
988
|
+
setData([]);
|
|
989
|
+
setError(null);
|
|
990
|
+
}, []);
|
|
991
|
+
return { data, search, clear, isLoading, error };
|
|
992
|
+
}
|
|
993
|
+
function useMemorySubscription(options) {
|
|
994
|
+
const { ws, isConnected } = useCilow();
|
|
995
|
+
const [latestEvent, setLatestEvent] = react.useState(null);
|
|
996
|
+
const [events, setEvents] = react.useState([]);
|
|
997
|
+
react.useEffect(() => {
|
|
998
|
+
if (!ws) return;
|
|
999
|
+
const unsubscribers = [];
|
|
1000
|
+
const handleEvent = (event) => {
|
|
1001
|
+
setLatestEvent(event);
|
|
1002
|
+
setEvents((prev) => [...prev, event]);
|
|
1003
|
+
};
|
|
1004
|
+
const eventTypes = options?.eventTypes ?? [
|
|
1005
|
+
"memory.created",
|
|
1006
|
+
"memory.updated",
|
|
1007
|
+
"memory.deleted"
|
|
1008
|
+
];
|
|
1009
|
+
for (const eventType of eventTypes) {
|
|
1010
|
+
if (eventType === "memory.created") {
|
|
1011
|
+
unsubscribers.push(ws.onMemoryCreated(handleEvent));
|
|
1012
|
+
} else if (eventType === "memory.updated") {
|
|
1013
|
+
unsubscribers.push(ws.onMemoryUpdated(handleEvent));
|
|
1014
|
+
} else if (eventType === "memory.deleted") {
|
|
1015
|
+
unsubscribers.push(ws.onMemoryDeleted(handleEvent));
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
if (options?.userId || options?.sessionId || options?.tags) {
|
|
1019
|
+
ws.subscribe({
|
|
1020
|
+
userId: options.userId,
|
|
1021
|
+
sessionId: options.sessionId,
|
|
1022
|
+
tags: options.tags
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
return () => {
|
|
1026
|
+
unsubscribers.forEach((unsub) => unsub());
|
|
1027
|
+
};
|
|
1028
|
+
}, [ws, options?.userId, options?.sessionId, options?.tags, options?.eventTypes]);
|
|
1029
|
+
const clearEvents = react.useCallback(() => {
|
|
1030
|
+
setEvents([]);
|
|
1031
|
+
setLatestEvent(null);
|
|
1032
|
+
}, []);
|
|
1033
|
+
return { latestEvent, events, isConnected, clearEvents };
|
|
1034
|
+
}
|
|
1035
|
+
function useMemoryContext() {
|
|
1036
|
+
const { client, userId } = useCilow();
|
|
1037
|
+
const [context, setContext] = react.useState("");
|
|
1038
|
+
const [memoriesUsed, setMemoriesUsed] = react.useState(0);
|
|
1039
|
+
const [isLoading, setIsLoading] = react.useState(false);
|
|
1040
|
+
const [error, setError] = react.useState(null);
|
|
1041
|
+
const getContext = react.useCallback(
|
|
1042
|
+
async (query, options) => {
|
|
1043
|
+
setIsLoading(true);
|
|
1044
|
+
setError(null);
|
|
1045
|
+
try {
|
|
1046
|
+
const result = await client.getContext(query, {
|
|
1047
|
+
...options,
|
|
1048
|
+
userId
|
|
1049
|
+
});
|
|
1050
|
+
setContext(result.context);
|
|
1051
|
+
setMemoriesUsed(result.memoriesUsed);
|
|
1052
|
+
return { context: result.context, memoriesUsed: result.memoriesUsed };
|
|
1053
|
+
} catch (err) {
|
|
1054
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
1055
|
+
setError(error2);
|
|
1056
|
+
throw error2;
|
|
1057
|
+
} finally {
|
|
1058
|
+
setIsLoading(false);
|
|
1059
|
+
}
|
|
1060
|
+
},
|
|
1061
|
+
[client, userId]
|
|
1062
|
+
);
|
|
1063
|
+
const clearContext = react.useCallback(() => {
|
|
1064
|
+
setContext("");
|
|
1065
|
+
setMemoriesUsed(0);
|
|
1066
|
+
}, []);
|
|
1067
|
+
return { getContext, context, memoriesUsed, clearContext, isLoading, error };
|
|
1068
|
+
}
|
|
1069
|
+
function useMemoryStats() {
|
|
1070
|
+
const { client } = useCilow();
|
|
1071
|
+
const [stats, setStats] = react.useState(null);
|
|
1072
|
+
const [isLoading, setIsLoading] = react.useState(false);
|
|
1073
|
+
const [error, setError] = react.useState(null);
|
|
1074
|
+
const refresh = react.useCallback(async () => {
|
|
1075
|
+
setIsLoading(true);
|
|
1076
|
+
setError(null);
|
|
1077
|
+
try {
|
|
1078
|
+
const newStats = await client.getStats();
|
|
1079
|
+
setStats(newStats);
|
|
1080
|
+
} catch (err) {
|
|
1081
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
1082
|
+
setError(error2);
|
|
1083
|
+
} finally {
|
|
1084
|
+
setIsLoading(false);
|
|
1085
|
+
}
|
|
1086
|
+
}, [client]);
|
|
1087
|
+
react.useEffect(() => {
|
|
1088
|
+
refresh();
|
|
1089
|
+
}, [refresh]);
|
|
1090
|
+
return { stats, refresh, isLoading, error };
|
|
1091
|
+
}
|
|
1092
|
+
function useConversation() {
|
|
1093
|
+
const { client, userId, sessionId } = useCilow();
|
|
1094
|
+
const [isLoading, setIsLoading] = react.useState(false);
|
|
1095
|
+
const [error, setError] = react.useState(null);
|
|
1096
|
+
const [storedCount, setStoredCount] = react.useState(0);
|
|
1097
|
+
const storeConversation = react.useCallback(
|
|
1098
|
+
async (userMessage, assistantResponse, metadata) => {
|
|
1099
|
+
setIsLoading(true);
|
|
1100
|
+
setError(null);
|
|
1101
|
+
try {
|
|
1102
|
+
const memoryId = await client.storeConversation({
|
|
1103
|
+
userMessage,
|
|
1104
|
+
assistantResponse,
|
|
1105
|
+
userId: userId ?? "anonymous",
|
|
1106
|
+
sessionId,
|
|
1107
|
+
metadata
|
|
1108
|
+
});
|
|
1109
|
+
setStoredCount((c) => c + 1);
|
|
1110
|
+
return memoryId;
|
|
1111
|
+
} catch (err) {
|
|
1112
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
1113
|
+
setError(error2);
|
|
1114
|
+
throw error2;
|
|
1115
|
+
} finally {
|
|
1116
|
+
setIsLoading(false);
|
|
1117
|
+
}
|
|
1118
|
+
},
|
|
1119
|
+
[client, userId, sessionId]
|
|
1120
|
+
);
|
|
1121
|
+
return { storeConversation, storedCount, isLoading, error };
|
|
1122
|
+
}
|
|
1123
|
+
function useDebounce(value, delay) {
|
|
1124
|
+
const [debouncedValue, setDebouncedValue] = react.useState(value);
|
|
1125
|
+
react.useEffect(() => {
|
|
1126
|
+
const timer = setTimeout(() => setDebouncedValue(value), delay);
|
|
1127
|
+
return () => clearTimeout(timer);
|
|
1128
|
+
}, [value, delay]);
|
|
1129
|
+
return debouncedValue;
|
|
1130
|
+
}
|
|
1131
|
+
function useDebouncedSearch(options) {
|
|
1132
|
+
const { client, userId } = useCilow();
|
|
1133
|
+
const [query, setQuery] = react.useState("");
|
|
1134
|
+
const [results, setResults] = react.useState([]);
|
|
1135
|
+
const [isLoading, setIsLoading] = react.useState(false);
|
|
1136
|
+
const [error, setError] = react.useState(null);
|
|
1137
|
+
const debouncedQuery = useDebounce(query, options?.delay ?? 300);
|
|
1138
|
+
const minLength = options?.minLength ?? 2;
|
|
1139
|
+
const limit = options?.limit ?? 10;
|
|
1140
|
+
react.useEffect(() => {
|
|
1141
|
+
if (debouncedQuery.length < minLength) {
|
|
1142
|
+
setResults([]);
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
const search = async () => {
|
|
1146
|
+
setIsLoading(true);
|
|
1147
|
+
setError(null);
|
|
1148
|
+
try {
|
|
1149
|
+
const searchResults = await client.recall(debouncedQuery, {
|
|
1150
|
+
limit,
|
|
1151
|
+
userId
|
|
1152
|
+
});
|
|
1153
|
+
setResults(searchResults);
|
|
1154
|
+
} catch (err) {
|
|
1155
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
1156
|
+
setError(error2);
|
|
1157
|
+
setResults([]);
|
|
1158
|
+
} finally {
|
|
1159
|
+
setIsLoading(false);
|
|
1160
|
+
}
|
|
1161
|
+
};
|
|
1162
|
+
search();
|
|
1163
|
+
}, [debouncedQuery, client, userId, limit, minLength]);
|
|
1164
|
+
const clear = react.useCallback(() => {
|
|
1165
|
+
setQuery("");
|
|
1166
|
+
setResults([]);
|
|
1167
|
+
setError(null);
|
|
1168
|
+
}, []);
|
|
1169
|
+
return { query, setQuery, results, clear, isLoading, error };
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
exports.CilowProvider = CilowProvider;
|
|
1173
|
+
exports.useCilow = useCilow;
|
|
1174
|
+
exports.useConversation = useConversation;
|
|
1175
|
+
exports.useDebounce = useDebounce;
|
|
1176
|
+
exports.useDebouncedSearch = useDebouncedSearch;
|
|
1177
|
+
exports.useMemory = useMemory;
|
|
1178
|
+
exports.useMemoryContext = useMemoryContext;
|
|
1179
|
+
exports.useMemoryStats = useMemoryStats;
|
|
1180
|
+
exports.useMemorySubscription = useMemorySubscription;
|
|
1181
|
+
exports.useRecall = useRecall;
|
|
1182
|
+
//# sourceMappingURL=hooks.js.map
|
|
1183
|
+
//# sourceMappingURL=hooks.js.map
|