@reverbia/sdk 1.0.0-next.20251124100226 → 1.0.0-next.20251125084053
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/react/index.cjs +875 -620
- package/dist/react/index.d.mts +78 -4
- package/dist/react/index.d.ts +78 -4
- package/dist/react/index.mjs +614 -365
- package/package.json +3 -1
package/dist/react/index.mjs
CHANGED
|
@@ -1,288 +1,5 @@
|
|
|
1
1
|
// src/react/useChat.ts
|
|
2
|
-
import { useCallback, useState } from "react";
|
|
3
|
-
import {
|
|
4
|
-
postApiV1ChatCompletions
|
|
5
|
-
} from "@reverbia/sdk";
|
|
6
|
-
function useChat(options) {
|
|
7
|
-
const { getToken } = options || {};
|
|
8
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
9
|
-
const sendMessage = useCallback(
|
|
10
|
-
async ({
|
|
11
|
-
messages,
|
|
12
|
-
model
|
|
13
|
-
}) => {
|
|
14
|
-
if (!messages?.length) {
|
|
15
|
-
const error = "messages are required to call sendMessage.";
|
|
16
|
-
return { data: null, error };
|
|
17
|
-
}
|
|
18
|
-
if (!model) {
|
|
19
|
-
const error = "model is required to call sendMessage.";
|
|
20
|
-
return { data: null, error };
|
|
21
|
-
}
|
|
22
|
-
if (!getToken) {
|
|
23
|
-
const error = "Token getter function is required.";
|
|
24
|
-
return { data: null, error };
|
|
25
|
-
}
|
|
26
|
-
setIsLoading(true);
|
|
27
|
-
try {
|
|
28
|
-
const token = await getToken();
|
|
29
|
-
if (!token) {
|
|
30
|
-
const error = "No access token available.";
|
|
31
|
-
setIsLoading(false);
|
|
32
|
-
return { data: null, error };
|
|
33
|
-
}
|
|
34
|
-
const completion = await postApiV1ChatCompletions({
|
|
35
|
-
body: {
|
|
36
|
-
messages,
|
|
37
|
-
model
|
|
38
|
-
},
|
|
39
|
-
headers: {
|
|
40
|
-
Authorization: `Bearer ${token}`
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
if (!completion.data) {
|
|
44
|
-
const error = completion.error?.error ?? "API did not return a completion response.";
|
|
45
|
-
setIsLoading(false);
|
|
46
|
-
return { data: null, error };
|
|
47
|
-
}
|
|
48
|
-
if (typeof completion.data === "string") {
|
|
49
|
-
const error = "API returned a string response instead of a completion object.";
|
|
50
|
-
setIsLoading(false);
|
|
51
|
-
return { data: null, error };
|
|
52
|
-
}
|
|
53
|
-
setIsLoading(false);
|
|
54
|
-
return { data: completion.data, error: null };
|
|
55
|
-
} catch (err) {
|
|
56
|
-
const error = err instanceof Error ? err.message : "Failed to send message.";
|
|
57
|
-
setIsLoading(false);
|
|
58
|
-
return { data: null, error };
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
[getToken]
|
|
62
|
-
);
|
|
63
|
-
return {
|
|
64
|
-
isLoading,
|
|
65
|
-
sendMessage
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// src/react/useMemory.ts
|
|
70
|
-
import { useCallback as useCallback2, useRef } from "react";
|
|
71
|
-
import { postApiV1ChatCompletions as postApiV1ChatCompletions2 } from "@reverbia/sdk";
|
|
72
|
-
|
|
73
|
-
// src/lib/memory/service.ts
|
|
74
|
-
var FACT_EXTRACTION_PROMPT = `You are a memory extraction system. Extract durable user memories from chat messages.
|
|
75
|
-
|
|
76
|
-
CRITICAL: You MUST respond with ONLY valid JSON. No explanations, no markdown, no code blocks, just pure JSON.
|
|
77
|
-
|
|
78
|
-
Only extract facts that will be useful in future conversations, such as identity, stable preferences, ongoing projects, skills, and constraints.
|
|
79
|
-
|
|
80
|
-
Do not extract sensitive attributes, temporary things, or single-use instructions.
|
|
81
|
-
|
|
82
|
-
If there are no memories to extract, return: {"items": []}
|
|
83
|
-
|
|
84
|
-
Response format (JSON only, no other text):
|
|
85
|
-
|
|
86
|
-
{
|
|
87
|
-
"items": [
|
|
88
|
-
{
|
|
89
|
-
"type": "identity",
|
|
90
|
-
"namespace": "identity",
|
|
91
|
-
"key": "name",
|
|
92
|
-
"value": "Charlie",
|
|
93
|
-
"rawEvidence": "I'm Charlie",
|
|
94
|
-
"confidence": 0.98,
|
|
95
|
-
"pii": true
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
"type": "identity",
|
|
99
|
-
"namespace": "work",
|
|
100
|
-
"key": "company",
|
|
101
|
-
"value": "ZetaChain",
|
|
102
|
-
"rawEvidence": "called ZetaChain",
|
|
103
|
-
"confidence": 0.99,
|
|
104
|
-
"pii": false
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
"type": "preference",
|
|
108
|
-
"namespace": "answer_style",
|
|
109
|
-
"key": "verbosity",
|
|
110
|
-
"value": "concise_direct",
|
|
111
|
-
"rawEvidence": "I prefer concise, direct answers",
|
|
112
|
-
"confidence": 0.96,
|
|
113
|
-
"pii": false
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
"type": "identity",
|
|
117
|
-
"namespace": "timezone",
|
|
118
|
-
"key": "tz",
|
|
119
|
-
"value": "America/Los_Angeles",
|
|
120
|
-
"rawEvidence": "I'm in PST",
|
|
121
|
-
"confidence": 0.9,
|
|
122
|
-
"pii": false
|
|
123
|
-
}
|
|
124
|
-
]
|
|
125
|
-
}`;
|
|
126
|
-
var preprocessMemories = (items, minConfidence = 0.6) => {
|
|
127
|
-
if (!items || !Array.isArray(items)) {
|
|
128
|
-
return [];
|
|
129
|
-
}
|
|
130
|
-
const validItems = items.filter((item) => {
|
|
131
|
-
if (item.namespace == null || item.key == null || item.value == null) {
|
|
132
|
-
console.warn(
|
|
133
|
-
"Dropping memory item with null/undefined namespace, key, or value:",
|
|
134
|
-
item
|
|
135
|
-
);
|
|
136
|
-
return false;
|
|
137
|
-
}
|
|
138
|
-
const namespace = String(item.namespace).trim();
|
|
139
|
-
const key = String(item.key).trim();
|
|
140
|
-
const value = String(item.value).trim();
|
|
141
|
-
if (namespace === "" || key === "" || value === "") {
|
|
142
|
-
console.warn(
|
|
143
|
-
"Dropping memory item with empty namespace, key, or value after trimming:",
|
|
144
|
-
item
|
|
145
|
-
);
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
if (typeof item.confidence !== "number" || item.confidence < minConfidence) {
|
|
149
|
-
console.warn(
|
|
150
|
-
`Dropping memory item with confidence ${item.confidence} below threshold ${minConfidence}:`,
|
|
151
|
-
item
|
|
152
|
-
);
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
return true;
|
|
156
|
-
});
|
|
157
|
-
const deduplicatedMap = /* @__PURE__ */ new Map();
|
|
158
|
-
for (const item of validItems) {
|
|
159
|
-
const uniqueKey = `${item.namespace}:${item.key}:${item.value}`;
|
|
160
|
-
const existing = deduplicatedMap.get(uniqueKey);
|
|
161
|
-
if (!existing || item.confidence > existing.confidence) {
|
|
162
|
-
deduplicatedMap.set(uniqueKey, item);
|
|
163
|
-
} else {
|
|
164
|
-
console.debug(
|
|
165
|
-
`Deduplicating memory item: keeping entry with higher confidence (${existing.confidence} > ${item.confidence})`,
|
|
166
|
-
{ namespace: item.namespace, key: item.key, value: item.value }
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
return Array.from(deduplicatedMap.values());
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
// src/lib/memory/db.ts
|
|
174
|
-
import Dexie from "dexie";
|
|
175
|
-
var MemoryDatabase = class extends Dexie {
|
|
176
|
-
constructor() {
|
|
177
|
-
super("MemoryDatabase");
|
|
178
|
-
this.version(2).stores({
|
|
179
|
-
memories: "++id, uniqueKey, compositeKey, namespace, key, type, createdAt, updatedAt"
|
|
180
|
-
});
|
|
181
|
-
this.version(3).stores({
|
|
182
|
-
memories: "++id, uniqueKey, compositeKey, namespace, key, type, createdAt, updatedAt"
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
};
|
|
186
|
-
var memoryDb = new MemoryDatabase();
|
|
187
|
-
var saveMemory = async (memory) => {
|
|
188
|
-
const compositeKey = `${memory.namespace}:${memory.key}`;
|
|
189
|
-
const uniqueKey = `${memory.namespace}:${memory.key}:${memory.value}`;
|
|
190
|
-
const now = Date.now();
|
|
191
|
-
const existing = await memoryDb.memories.where("uniqueKey").equals(uniqueKey).first();
|
|
192
|
-
if (existing) {
|
|
193
|
-
const shouldPreserveEmbedding = existing.value === memory.value && existing.rawEvidence === memory.rawEvidence && existing.type === memory.type && existing.namespace === memory.namespace && existing.key === memory.key && existing.embedding !== void 0 && existing.embedding.length > 0;
|
|
194
|
-
const updateData = {
|
|
195
|
-
...memory,
|
|
196
|
-
compositeKey,
|
|
197
|
-
uniqueKey,
|
|
198
|
-
updatedAt: now,
|
|
199
|
-
createdAt: existing.createdAt
|
|
200
|
-
};
|
|
201
|
-
if (shouldPreserveEmbedding) {
|
|
202
|
-
updateData.embedding = existing.embedding;
|
|
203
|
-
updateData.embeddingModel = existing.embeddingModel;
|
|
204
|
-
} else {
|
|
205
|
-
updateData.embedding = [];
|
|
206
|
-
updateData.embeddingModel = void 0;
|
|
207
|
-
}
|
|
208
|
-
await memoryDb.memories.update(existing.id, updateData);
|
|
209
|
-
} else {
|
|
210
|
-
await memoryDb.memories.add({
|
|
211
|
-
...memory,
|
|
212
|
-
compositeKey,
|
|
213
|
-
uniqueKey,
|
|
214
|
-
createdAt: now,
|
|
215
|
-
updatedAt: now
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
};
|
|
219
|
-
var saveMemories = async (memories) => {
|
|
220
|
-
await Promise.all(memories.map((memory) => saveMemory(memory)));
|
|
221
|
-
};
|
|
222
|
-
var getAllMemories = async () => {
|
|
223
|
-
return memoryDb.memories.toArray();
|
|
224
|
-
};
|
|
225
|
-
var cosineSimilarity = (a, b) => {
|
|
226
|
-
if (a.length !== b.length) {
|
|
227
|
-
throw new Error("Vectors must have the same length");
|
|
228
|
-
}
|
|
229
|
-
let dotProduct = 0;
|
|
230
|
-
let normA = 0;
|
|
231
|
-
let normB = 0;
|
|
232
|
-
for (let i = 0; i < a.length; i++) {
|
|
233
|
-
dotProduct += a[i] * b[i];
|
|
234
|
-
normA += a[i] * a[i];
|
|
235
|
-
normB += b[i] * b[i];
|
|
236
|
-
}
|
|
237
|
-
const denominator = Math.sqrt(normA) * Math.sqrt(normB);
|
|
238
|
-
if (denominator === 0) {
|
|
239
|
-
return 0;
|
|
240
|
-
}
|
|
241
|
-
return dotProduct / denominator;
|
|
242
|
-
};
|
|
243
|
-
var searchSimilarMemories = async (queryEmbedding, limit = 10, minSimilarity = 0.6) => {
|
|
244
|
-
const allMemories = await getAllMemories();
|
|
245
|
-
const memoriesWithEmbeddings = allMemories.filter(
|
|
246
|
-
(m) => m.embedding && m.embedding.length > 0
|
|
247
|
-
);
|
|
248
|
-
console.log(
|
|
249
|
-
`[Memory Search] Total memories: ${allMemories.length}, memories with embeddings: ${memoriesWithEmbeddings.length}`
|
|
250
|
-
);
|
|
251
|
-
if (memoriesWithEmbeddings.length === 0) {
|
|
252
|
-
console.warn(
|
|
253
|
-
"[Memory Search] No memories with embeddings found. Memories may need embeddings generated. Use generateAndStoreEmbeddings() to generate embeddings for existing memories."
|
|
254
|
-
);
|
|
255
|
-
return [];
|
|
256
|
-
}
|
|
257
|
-
const allResults = memoriesWithEmbeddings.map((memory) => {
|
|
258
|
-
const similarity = cosineSimilarity(queryEmbedding, memory.embedding);
|
|
259
|
-
return {
|
|
260
|
-
...memory,
|
|
261
|
-
similarity
|
|
262
|
-
};
|
|
263
|
-
}).sort((a, b) => b.similarity - a.similarity);
|
|
264
|
-
console.log(
|
|
265
|
-
`[Memory Search] All similarity scores:`,
|
|
266
|
-
allResults.map((r) => ({
|
|
267
|
-
key: `${r.namespace}:${r.key}`,
|
|
268
|
-
value: r.value,
|
|
269
|
-
similarity: r.similarity.toFixed(4)
|
|
270
|
-
}))
|
|
271
|
-
);
|
|
272
|
-
const results = allResults.filter((result) => result.similarity >= minSimilarity).slice(0, limit);
|
|
273
|
-
if (results.length === 0 && allResults.length > 0) {
|
|
274
|
-
const topSimilarity = allResults[0].similarity;
|
|
275
|
-
const suggestedThreshold = Math.max(0.3, topSimilarity - 0.1);
|
|
276
|
-
console.warn(
|
|
277
|
-
`[Memory Search] No memories above threshold ${minSimilarity}. Highest similarity was ${topSimilarity.toFixed(4)}. Consider lowering the threshold to ${suggestedThreshold.toFixed(2)}`
|
|
278
|
-
);
|
|
279
|
-
} else {
|
|
280
|
-
console.log(
|
|
281
|
-
`[Memory Search] Found ${results.length} memories above similarity threshold ${minSimilarity}. Top similarity: ${results[0]?.similarity.toFixed(4) || "N/A"}`
|
|
282
|
-
);
|
|
283
|
-
}
|
|
284
|
-
return results;
|
|
285
|
-
};
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
286
3
|
|
|
287
4
|
// src/client/core/bodySerializer.gen.ts
|
|
288
5
|
var jsonBodySerializer = {
|
|
@@ -1012,91 +729,619 @@ var createClient = (config = {}) => {
|
|
|
1012
729
|
...result
|
|
1013
730
|
};
|
|
1014
731
|
}
|
|
1015
|
-
const textError = await response.text();
|
|
1016
|
-
let jsonError;
|
|
1017
|
-
try {
|
|
1018
|
-
jsonError = JSON.parse(textError);
|
|
1019
|
-
} catch {
|
|
732
|
+
const textError = await response.text();
|
|
733
|
+
let jsonError;
|
|
734
|
+
try {
|
|
735
|
+
jsonError = JSON.parse(textError);
|
|
736
|
+
} catch {
|
|
737
|
+
}
|
|
738
|
+
const error = jsonError ?? textError;
|
|
739
|
+
let finalError = error;
|
|
740
|
+
for (const fn of interceptors.error.fns) {
|
|
741
|
+
if (fn) {
|
|
742
|
+
finalError = await fn(error, response, opts);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
finalError = finalError || {};
|
|
746
|
+
if (opts.throwOnError) {
|
|
747
|
+
throw finalError;
|
|
748
|
+
}
|
|
749
|
+
return {
|
|
750
|
+
error: finalError,
|
|
751
|
+
...result
|
|
752
|
+
};
|
|
753
|
+
};
|
|
754
|
+
const makeMethodFn = (method) => (options) => request({ ...options, method });
|
|
755
|
+
const makeSseFn = (method) => async (options) => {
|
|
756
|
+
const { opts, url } = await beforeRequest(options);
|
|
757
|
+
return createSseClient({
|
|
758
|
+
...opts,
|
|
759
|
+
body: opts.body,
|
|
760
|
+
headers: opts.headers,
|
|
761
|
+
method,
|
|
762
|
+
onRequest: async (url2, init) => {
|
|
763
|
+
let request2 = new Request(url2, init);
|
|
764
|
+
const requestInit = {
|
|
765
|
+
...init,
|
|
766
|
+
method: init.method,
|
|
767
|
+
url: url2
|
|
768
|
+
};
|
|
769
|
+
for (const fn of interceptors.request.fns) {
|
|
770
|
+
if (fn) {
|
|
771
|
+
await fn(requestInit);
|
|
772
|
+
request2 = new Request(requestInit.url, requestInit);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
return request2;
|
|
776
|
+
},
|
|
777
|
+
url
|
|
778
|
+
});
|
|
779
|
+
};
|
|
780
|
+
return {
|
|
781
|
+
buildUrl,
|
|
782
|
+
connect: makeMethodFn("CONNECT"),
|
|
783
|
+
delete: makeMethodFn("DELETE"),
|
|
784
|
+
get: makeMethodFn("GET"),
|
|
785
|
+
getConfig,
|
|
786
|
+
head: makeMethodFn("HEAD"),
|
|
787
|
+
interceptors,
|
|
788
|
+
options: makeMethodFn("OPTIONS"),
|
|
789
|
+
patch: makeMethodFn("PATCH"),
|
|
790
|
+
post: makeMethodFn("POST"),
|
|
791
|
+
put: makeMethodFn("PUT"),
|
|
792
|
+
request,
|
|
793
|
+
setConfig,
|
|
794
|
+
sse: {
|
|
795
|
+
connect: makeSseFn("CONNECT"),
|
|
796
|
+
delete: makeSseFn("DELETE"),
|
|
797
|
+
get: makeSseFn("GET"),
|
|
798
|
+
head: makeSseFn("HEAD"),
|
|
799
|
+
options: makeSseFn("OPTIONS"),
|
|
800
|
+
patch: makeSseFn("PATCH"),
|
|
801
|
+
post: makeSseFn("POST"),
|
|
802
|
+
put: makeSseFn("PUT"),
|
|
803
|
+
trace: makeSseFn("TRACE")
|
|
804
|
+
},
|
|
805
|
+
trace: makeMethodFn("TRACE")
|
|
806
|
+
};
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
// src/clientConfig.ts
|
|
810
|
+
var createClientConfig = (config) => ({
|
|
811
|
+
...config,
|
|
812
|
+
baseUrl: "https://ai-portal-dev.zetachain.com"
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
// src/client/client.gen.ts
|
|
816
|
+
var client = createClient(createClientConfig(createConfig()));
|
|
817
|
+
|
|
818
|
+
// src/react/useChat.ts
|
|
819
|
+
function useChat(options) {
|
|
820
|
+
const { getToken, onData: globalOnData, onFinish, onError } = options || {};
|
|
821
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
822
|
+
const abortControllerRef = useRef(null);
|
|
823
|
+
const stop = useCallback(() => {
|
|
824
|
+
if (abortControllerRef.current) {
|
|
825
|
+
abortControllerRef.current.abort();
|
|
826
|
+
abortControllerRef.current = null;
|
|
827
|
+
}
|
|
828
|
+
}, []);
|
|
829
|
+
useEffect(() => {
|
|
830
|
+
return () => {
|
|
831
|
+
if (abortControllerRef.current) {
|
|
832
|
+
abortControllerRef.current.abort();
|
|
833
|
+
abortControllerRef.current = null;
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
}, []);
|
|
837
|
+
const sendMessage = useCallback(
|
|
838
|
+
async ({
|
|
839
|
+
messages,
|
|
840
|
+
model,
|
|
841
|
+
onData
|
|
842
|
+
}) => {
|
|
843
|
+
if (!messages?.length) {
|
|
844
|
+
const errorMsg = "messages are required to call sendMessage.";
|
|
845
|
+
if (onError) onError(new Error(errorMsg));
|
|
846
|
+
return { data: null, error: errorMsg };
|
|
847
|
+
}
|
|
848
|
+
if (!model) {
|
|
849
|
+
const errorMsg = "model is required to call sendMessage.";
|
|
850
|
+
if (onError) onError(new Error(errorMsg));
|
|
851
|
+
return { data: null, error: errorMsg };
|
|
852
|
+
}
|
|
853
|
+
if (!getToken) {
|
|
854
|
+
const errorMsg = "Token getter function is required.";
|
|
855
|
+
if (onError) onError(new Error(errorMsg));
|
|
856
|
+
return { data: null, error: errorMsg };
|
|
857
|
+
}
|
|
858
|
+
if (abortControllerRef.current) {
|
|
859
|
+
abortControllerRef.current.abort();
|
|
860
|
+
}
|
|
861
|
+
const abortController = new AbortController();
|
|
862
|
+
abortControllerRef.current = abortController;
|
|
863
|
+
setIsLoading(true);
|
|
864
|
+
try {
|
|
865
|
+
const token = await getToken();
|
|
866
|
+
if (!token) {
|
|
867
|
+
const errorMsg = "No access token available.";
|
|
868
|
+
setIsLoading(false);
|
|
869
|
+
if (onError) onError(new Error(errorMsg));
|
|
870
|
+
return { data: null, error: errorMsg };
|
|
871
|
+
}
|
|
872
|
+
const sseResult = await client.sse.post({
|
|
873
|
+
url: "/api/v1/chat/completions",
|
|
874
|
+
body: {
|
|
875
|
+
messages,
|
|
876
|
+
model,
|
|
877
|
+
stream: true
|
|
878
|
+
},
|
|
879
|
+
headers: {
|
|
880
|
+
"Content-Type": "application/json",
|
|
881
|
+
Authorization: `Bearer ${token}`
|
|
882
|
+
},
|
|
883
|
+
signal: abortController.signal
|
|
884
|
+
});
|
|
885
|
+
let accumulatedContent = "";
|
|
886
|
+
let completionId = "";
|
|
887
|
+
let completionModel = "";
|
|
888
|
+
let usage;
|
|
889
|
+
let finishReason;
|
|
890
|
+
for await (const chunk of sseResult.stream) {
|
|
891
|
+
if (typeof chunk === "string" && (chunk.trim() === "[DONE]" || chunk.includes("[DONE]"))) {
|
|
892
|
+
continue;
|
|
893
|
+
}
|
|
894
|
+
if (chunk && typeof chunk === "object") {
|
|
895
|
+
const chunkData = chunk;
|
|
896
|
+
if (chunkData.id && !completionId) {
|
|
897
|
+
completionId = chunkData.id;
|
|
898
|
+
}
|
|
899
|
+
if (chunkData.model && !completionModel) {
|
|
900
|
+
completionModel = chunkData.model;
|
|
901
|
+
}
|
|
902
|
+
if (chunkData.usage) {
|
|
903
|
+
usage = chunkData.usage;
|
|
904
|
+
}
|
|
905
|
+
if (chunkData.choices && Array.isArray(chunkData.choices) && chunkData.choices.length > 0) {
|
|
906
|
+
const choice = chunkData.choices[0];
|
|
907
|
+
if (choice.delta?.content) {
|
|
908
|
+
const content = choice.delta.content;
|
|
909
|
+
accumulatedContent += content;
|
|
910
|
+
if (onData) {
|
|
911
|
+
onData(content);
|
|
912
|
+
}
|
|
913
|
+
if (globalOnData) {
|
|
914
|
+
globalOnData(content);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
if (choice.finish_reason) {
|
|
918
|
+
finishReason = choice.finish_reason;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
const completion = {
|
|
924
|
+
id: completionId,
|
|
925
|
+
model: completionModel,
|
|
926
|
+
choices: [
|
|
927
|
+
{
|
|
928
|
+
index: 0,
|
|
929
|
+
message: {
|
|
930
|
+
role: "assistant",
|
|
931
|
+
content: accumulatedContent
|
|
932
|
+
},
|
|
933
|
+
finish_reason: finishReason
|
|
934
|
+
}
|
|
935
|
+
],
|
|
936
|
+
usage
|
|
937
|
+
};
|
|
938
|
+
setIsLoading(false);
|
|
939
|
+
if (onFinish) {
|
|
940
|
+
onFinish(completion);
|
|
941
|
+
}
|
|
942
|
+
return { data: completion, error: null };
|
|
943
|
+
} catch (err) {
|
|
944
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
945
|
+
setIsLoading(false);
|
|
946
|
+
return { data: null, error: "Request aborted" };
|
|
947
|
+
}
|
|
948
|
+
const errorMsg = err instanceof Error ? err.message : "Failed to send message.";
|
|
949
|
+
const errorObj = err instanceof Error ? err : new Error(errorMsg);
|
|
950
|
+
setIsLoading(false);
|
|
951
|
+
if (onError) {
|
|
952
|
+
onError(errorObj);
|
|
953
|
+
}
|
|
954
|
+
return { data: null, error: errorMsg };
|
|
955
|
+
} finally {
|
|
956
|
+
if (abortControllerRef.current === abortController) {
|
|
957
|
+
abortControllerRef.current = null;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
},
|
|
961
|
+
[getToken, globalOnData, onFinish, onError]
|
|
962
|
+
);
|
|
963
|
+
return {
|
|
964
|
+
isLoading,
|
|
965
|
+
sendMessage,
|
|
966
|
+
stop
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
// src/react/useEncryption.ts
|
|
971
|
+
import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
972
|
+
import { useSignMessage, useWallets } from "@privy-io/react-auth";
|
|
973
|
+
var SIGN_MESSAGE = "The app is asking you to sign this message to generate a key, which will be used to encrypt data.";
|
|
974
|
+
var SIGNATURE_STORAGE_KEY = "privy_encryption_key";
|
|
975
|
+
function getStorageItem(key) {
|
|
976
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
977
|
+
return null;
|
|
978
|
+
}
|
|
979
|
+
try {
|
|
980
|
+
return localStorage.getItem(key);
|
|
981
|
+
} catch {
|
|
982
|
+
return null;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
function setStorageItem(key, value) {
|
|
986
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
987
|
+
return false;
|
|
988
|
+
}
|
|
989
|
+
try {
|
|
990
|
+
localStorage.setItem(key, value);
|
|
991
|
+
return true;
|
|
992
|
+
} catch {
|
|
993
|
+
return false;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
function hexToBytes(hex) {
|
|
997
|
+
const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
998
|
+
const bytes = new Uint8Array(cleanHex.length / 2);
|
|
999
|
+
for (let i = 0; i < cleanHex.length; i += 2) {
|
|
1000
|
+
bytes[i / 2] = parseInt(cleanHex.slice(i, i + 2), 16);
|
|
1001
|
+
}
|
|
1002
|
+
return bytes;
|
|
1003
|
+
}
|
|
1004
|
+
function bytesToHex(bytes) {
|
|
1005
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1006
|
+
}
|
|
1007
|
+
async function deriveKeyFromSignature(signature) {
|
|
1008
|
+
const sigBytes = hexToBytes(signature);
|
|
1009
|
+
const hashBuffer = await crypto.subtle.digest(
|
|
1010
|
+
"SHA-256",
|
|
1011
|
+
sigBytes.buffer
|
|
1012
|
+
);
|
|
1013
|
+
const hashBytes = new Uint8Array(hashBuffer);
|
|
1014
|
+
return bytesToHex(hashBytes);
|
|
1015
|
+
}
|
|
1016
|
+
async function getEncryptionKey() {
|
|
1017
|
+
const keyHex = getStorageItem(SIGNATURE_STORAGE_KEY);
|
|
1018
|
+
if (!keyHex) {
|
|
1019
|
+
throw new Error("Encryption key not found. Please sign in first.");
|
|
1020
|
+
}
|
|
1021
|
+
const keyBytes = hexToBytes(keyHex);
|
|
1022
|
+
return crypto.subtle.importKey(
|
|
1023
|
+
"raw",
|
|
1024
|
+
keyBytes.buffer,
|
|
1025
|
+
{ name: "AES-GCM" },
|
|
1026
|
+
false,
|
|
1027
|
+
["encrypt", "decrypt"]
|
|
1028
|
+
);
|
|
1029
|
+
}
|
|
1030
|
+
async function encryptData(plaintext) {
|
|
1031
|
+
const key = await getEncryptionKey();
|
|
1032
|
+
const plaintextBytes = typeof plaintext === "string" ? new TextEncoder().encode(plaintext) : plaintext;
|
|
1033
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
1034
|
+
const encryptedData = await crypto.subtle.encrypt(
|
|
1035
|
+
{
|
|
1036
|
+
name: "AES-GCM",
|
|
1037
|
+
iv
|
|
1038
|
+
},
|
|
1039
|
+
key,
|
|
1040
|
+
plaintextBytes.buffer
|
|
1041
|
+
);
|
|
1042
|
+
const encryptedBytes = new Uint8Array(encryptedData);
|
|
1043
|
+
const combined = new Uint8Array(iv.length + encryptedBytes.length);
|
|
1044
|
+
combined.set(iv, 0);
|
|
1045
|
+
combined.set(encryptedBytes, iv.length);
|
|
1046
|
+
return bytesToHex(combined);
|
|
1047
|
+
}
|
|
1048
|
+
async function decryptData(encryptedHex) {
|
|
1049
|
+
const key = await getEncryptionKey();
|
|
1050
|
+
const combined = hexToBytes(encryptedHex);
|
|
1051
|
+
const iv = combined.slice(0, 12);
|
|
1052
|
+
const encryptedData = combined.slice(12);
|
|
1053
|
+
const decryptedData = await crypto.subtle.decrypt(
|
|
1054
|
+
{
|
|
1055
|
+
name: "AES-GCM",
|
|
1056
|
+
iv
|
|
1057
|
+
},
|
|
1058
|
+
key,
|
|
1059
|
+
encryptedData
|
|
1060
|
+
);
|
|
1061
|
+
return new TextDecoder().decode(decryptedData);
|
|
1062
|
+
}
|
|
1063
|
+
async function decryptDataBytes(encryptedHex) {
|
|
1064
|
+
const key = await getEncryptionKey();
|
|
1065
|
+
const combined = hexToBytes(encryptedHex);
|
|
1066
|
+
const iv = combined.slice(0, 12);
|
|
1067
|
+
const encryptedData = combined.slice(12);
|
|
1068
|
+
const decryptedData = await crypto.subtle.decrypt(
|
|
1069
|
+
{
|
|
1070
|
+
name: "AES-GCM",
|
|
1071
|
+
iv
|
|
1072
|
+
},
|
|
1073
|
+
key,
|
|
1074
|
+
encryptedData
|
|
1075
|
+
);
|
|
1076
|
+
return new Uint8Array(decryptedData);
|
|
1077
|
+
}
|
|
1078
|
+
function useEncryption(authenticated) {
|
|
1079
|
+
const { signMessage } = useSignMessage();
|
|
1080
|
+
const { wallets } = useWallets();
|
|
1081
|
+
const hasRequestedSignature = useRef2(false);
|
|
1082
|
+
const hasCheckedStorage = useRef2(false);
|
|
1083
|
+
useEffect2(() => {
|
|
1084
|
+
if (!authenticated || wallets.length === 0) {
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
const existingKey = getStorageItem(SIGNATURE_STORAGE_KEY);
|
|
1088
|
+
if (existingKey) {
|
|
1089
|
+
if (!hasCheckedStorage.current) {
|
|
1090
|
+
hasCheckedStorage.current = true;
|
|
1091
|
+
}
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
const requestSignature = async () => {
|
|
1095
|
+
if (!hasRequestedSignature.current) {
|
|
1096
|
+
hasRequestedSignature.current = true;
|
|
1097
|
+
try {
|
|
1098
|
+
const { signature } = await signMessage(
|
|
1099
|
+
{ message: SIGN_MESSAGE },
|
|
1100
|
+
{
|
|
1101
|
+
address: wallets[0].address
|
|
1102
|
+
}
|
|
1103
|
+
);
|
|
1104
|
+
const encryptionKey = await deriveKeyFromSignature(signature);
|
|
1105
|
+
const stored = setStorageItem(SIGNATURE_STORAGE_KEY, encryptionKey);
|
|
1106
|
+
if (!stored) {
|
|
1107
|
+
throw new Error("Failed to store encryption key in localStorage");
|
|
1108
|
+
}
|
|
1109
|
+
} catch (error) {
|
|
1110
|
+
hasRequestedSignature.current = false;
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
};
|
|
1114
|
+
requestSignature();
|
|
1115
|
+
}, [
|
|
1116
|
+
authenticated,
|
|
1117
|
+
wallets.length > 0 ? wallets[0]?.address : null,
|
|
1118
|
+
signMessage
|
|
1119
|
+
]);
|
|
1120
|
+
useEffect2(() => {
|
|
1121
|
+
if (!authenticated) {
|
|
1122
|
+
hasRequestedSignature.current = false;
|
|
1123
|
+
hasCheckedStorage.current = false;
|
|
1124
|
+
}
|
|
1125
|
+
}, [authenticated]);
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
// src/react/useMemory.ts
|
|
1129
|
+
import { useCallback as useCallback2, useRef as useRef3 } from "react";
|
|
1130
|
+
import { postApiV1ChatCompletions } from "@reverbia/sdk";
|
|
1131
|
+
|
|
1132
|
+
// src/lib/memory/service.ts
|
|
1133
|
+
var FACT_EXTRACTION_PROMPT = `You are a memory extraction system. Extract durable user memories from chat messages.
|
|
1134
|
+
|
|
1135
|
+
CRITICAL: You MUST respond with ONLY valid JSON. No explanations, no markdown, no code blocks, just pure JSON.
|
|
1136
|
+
|
|
1137
|
+
Only extract facts that will be useful in future conversations, such as identity, stable preferences, ongoing projects, skills, and constraints.
|
|
1138
|
+
|
|
1139
|
+
Do not extract sensitive attributes, temporary things, or single-use instructions.
|
|
1140
|
+
|
|
1141
|
+
If there are no memories to extract, return: {"items": []}
|
|
1142
|
+
|
|
1143
|
+
Response format (JSON only, no other text):
|
|
1144
|
+
|
|
1145
|
+
{
|
|
1146
|
+
"items": [
|
|
1147
|
+
{
|
|
1148
|
+
"type": "identity",
|
|
1149
|
+
"namespace": "identity",
|
|
1150
|
+
"key": "name",
|
|
1151
|
+
"value": "Charlie",
|
|
1152
|
+
"rawEvidence": "I'm Charlie",
|
|
1153
|
+
"confidence": 0.98,
|
|
1154
|
+
"pii": true
|
|
1155
|
+
},
|
|
1156
|
+
{
|
|
1157
|
+
"type": "identity",
|
|
1158
|
+
"namespace": "work",
|
|
1159
|
+
"key": "company",
|
|
1160
|
+
"value": "ZetaChain",
|
|
1161
|
+
"rawEvidence": "called ZetaChain",
|
|
1162
|
+
"confidence": 0.99,
|
|
1163
|
+
"pii": false
|
|
1164
|
+
},
|
|
1165
|
+
{
|
|
1166
|
+
"type": "preference",
|
|
1167
|
+
"namespace": "answer_style",
|
|
1168
|
+
"key": "verbosity",
|
|
1169
|
+
"value": "concise_direct",
|
|
1170
|
+
"rawEvidence": "I prefer concise, direct answers",
|
|
1171
|
+
"confidence": 0.96,
|
|
1172
|
+
"pii": false
|
|
1173
|
+
},
|
|
1174
|
+
{
|
|
1175
|
+
"type": "identity",
|
|
1176
|
+
"namespace": "timezone",
|
|
1177
|
+
"key": "tz",
|
|
1178
|
+
"value": "America/Los_Angeles",
|
|
1179
|
+
"rawEvidence": "I'm in PST",
|
|
1180
|
+
"confidence": 0.9,
|
|
1181
|
+
"pii": false
|
|
1182
|
+
}
|
|
1183
|
+
]
|
|
1184
|
+
}`;
|
|
1185
|
+
var preprocessMemories = (items, minConfidence = 0.6) => {
|
|
1186
|
+
if (!items || !Array.isArray(items)) {
|
|
1187
|
+
return [];
|
|
1188
|
+
}
|
|
1189
|
+
const validItems = items.filter((item) => {
|
|
1190
|
+
if (item.namespace == null || item.key == null || item.value == null) {
|
|
1191
|
+
console.warn(
|
|
1192
|
+
"Dropping memory item with null/undefined namespace, key, or value:",
|
|
1193
|
+
item
|
|
1194
|
+
);
|
|
1195
|
+
return false;
|
|
1020
1196
|
}
|
|
1021
|
-
const
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1197
|
+
const namespace = String(item.namespace).trim();
|
|
1198
|
+
const key = String(item.key).trim();
|
|
1199
|
+
const value = String(item.value).trim();
|
|
1200
|
+
if (namespace === "" || key === "" || value === "") {
|
|
1201
|
+
console.warn(
|
|
1202
|
+
"Dropping memory item with empty namespace, key, or value after trimming:",
|
|
1203
|
+
item
|
|
1204
|
+
);
|
|
1205
|
+
return false;
|
|
1027
1206
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1207
|
+
if (typeof item.confidence !== "number" || item.confidence < minConfidence) {
|
|
1208
|
+
console.warn(
|
|
1209
|
+
`Dropping memory item with confidence ${item.confidence} below threshold ${minConfidence}:`,
|
|
1210
|
+
item
|
|
1211
|
+
);
|
|
1212
|
+
return false;
|
|
1031
1213
|
}
|
|
1032
|
-
return
|
|
1033
|
-
|
|
1034
|
-
|
|
1214
|
+
return true;
|
|
1215
|
+
});
|
|
1216
|
+
const deduplicatedMap = /* @__PURE__ */ new Map();
|
|
1217
|
+
for (const item of validItems) {
|
|
1218
|
+
const uniqueKey = `${item.namespace}:${item.key}:${item.value}`;
|
|
1219
|
+
const existing = deduplicatedMap.get(uniqueKey);
|
|
1220
|
+
if (!existing || item.confidence > existing.confidence) {
|
|
1221
|
+
deduplicatedMap.set(uniqueKey, item);
|
|
1222
|
+
} else {
|
|
1223
|
+
console.debug(
|
|
1224
|
+
`Deduplicating memory item: keeping entry with higher confidence (${existing.confidence} > ${item.confidence})`,
|
|
1225
|
+
{ namespace: item.namespace, key: item.key, value: item.value }
|
|
1226
|
+
);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
return Array.from(deduplicatedMap.values());
|
|
1230
|
+
};
|
|
1231
|
+
|
|
1232
|
+
// src/lib/memory/db.ts
|
|
1233
|
+
import Dexie from "dexie";
|
|
1234
|
+
var MemoryDatabase = class extends Dexie {
|
|
1235
|
+
constructor() {
|
|
1236
|
+
super("MemoryDatabase");
|
|
1237
|
+
this.version(2).stores({
|
|
1238
|
+
memories: "++id, uniqueKey, compositeKey, namespace, key, type, createdAt, updatedAt"
|
|
1239
|
+
});
|
|
1240
|
+
this.version(3).stores({
|
|
1241
|
+
memories: "++id, uniqueKey, compositeKey, namespace, key, type, createdAt, updatedAt"
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
};
|
|
1245
|
+
var memoryDb = new MemoryDatabase();
|
|
1246
|
+
var saveMemory = async (memory) => {
|
|
1247
|
+
const compositeKey = `${memory.namespace}:${memory.key}`;
|
|
1248
|
+
const uniqueKey = `${memory.namespace}:${memory.key}:${memory.value}`;
|
|
1249
|
+
const now = Date.now();
|
|
1250
|
+
const existing = await memoryDb.memories.where("uniqueKey").equals(uniqueKey).first();
|
|
1251
|
+
if (existing) {
|
|
1252
|
+
const shouldPreserveEmbedding = existing.value === memory.value && existing.rawEvidence === memory.rawEvidence && existing.type === memory.type && existing.namespace === memory.namespace && existing.key === memory.key && existing.embedding !== void 0 && existing.embedding.length > 0;
|
|
1253
|
+
const updateData = {
|
|
1254
|
+
...memory,
|
|
1255
|
+
compositeKey,
|
|
1256
|
+
uniqueKey,
|
|
1257
|
+
updatedAt: now,
|
|
1258
|
+
createdAt: existing.createdAt
|
|
1035
1259
|
};
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
};
|
|
1052
|
-
for (const fn of interceptors.request.fns) {
|
|
1053
|
-
if (fn) {
|
|
1054
|
-
await fn(requestInit);
|
|
1055
|
-
request2 = new Request(requestInit.url, requestInit);
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
return request2;
|
|
1059
|
-
},
|
|
1060
|
-
url
|
|
1260
|
+
if (shouldPreserveEmbedding) {
|
|
1261
|
+
updateData.embedding = existing.embedding;
|
|
1262
|
+
updateData.embeddingModel = existing.embeddingModel;
|
|
1263
|
+
} else {
|
|
1264
|
+
updateData.embedding = [];
|
|
1265
|
+
updateData.embeddingModel = void 0;
|
|
1266
|
+
}
|
|
1267
|
+
await memoryDb.memories.update(existing.id, updateData);
|
|
1268
|
+
} else {
|
|
1269
|
+
await memoryDb.memories.add({
|
|
1270
|
+
...memory,
|
|
1271
|
+
compositeKey,
|
|
1272
|
+
uniqueKey,
|
|
1273
|
+
createdAt: now,
|
|
1274
|
+
updatedAt: now
|
|
1061
1275
|
});
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1276
|
+
}
|
|
1277
|
+
};
|
|
1278
|
+
var saveMemories = async (memories) => {
|
|
1279
|
+
await Promise.all(memories.map((memory) => saveMemory(memory)));
|
|
1280
|
+
};
|
|
1281
|
+
var getAllMemories = async () => {
|
|
1282
|
+
return memoryDb.memories.toArray();
|
|
1283
|
+
};
|
|
1284
|
+
var cosineSimilarity = (a, b) => {
|
|
1285
|
+
if (a.length !== b.length) {
|
|
1286
|
+
throw new Error("Vectors must have the same length");
|
|
1287
|
+
}
|
|
1288
|
+
let dotProduct = 0;
|
|
1289
|
+
let normA = 0;
|
|
1290
|
+
let normB = 0;
|
|
1291
|
+
for (let i = 0; i < a.length; i++) {
|
|
1292
|
+
dotProduct += a[i] * b[i];
|
|
1293
|
+
normA += a[i] * a[i];
|
|
1294
|
+
normB += b[i] * b[i];
|
|
1295
|
+
}
|
|
1296
|
+
const denominator = Math.sqrt(normA) * Math.sqrt(normB);
|
|
1297
|
+
if (denominator === 0) {
|
|
1298
|
+
return 0;
|
|
1299
|
+
}
|
|
1300
|
+
return dotProduct / denominator;
|
|
1301
|
+
};
|
|
1302
|
+
var searchSimilarMemories = async (queryEmbedding, limit = 10, minSimilarity = 0.6) => {
|
|
1303
|
+
const allMemories = await getAllMemories();
|
|
1304
|
+
const memoriesWithEmbeddings = allMemories.filter(
|
|
1305
|
+
(m) => m.embedding && m.embedding.length > 0
|
|
1306
|
+
);
|
|
1307
|
+
console.log(
|
|
1308
|
+
`[Memory Search] Total memories: ${allMemories.length}, memories with embeddings: ${memoriesWithEmbeddings.length}`
|
|
1309
|
+
);
|
|
1310
|
+
if (memoriesWithEmbeddings.length === 0) {
|
|
1311
|
+
console.warn(
|
|
1312
|
+
"[Memory Search] No memories with embeddings found. Memories may need embeddings generated. Use generateAndStoreEmbeddings() to generate embeddings for existing memories."
|
|
1313
|
+
);
|
|
1314
|
+
return [];
|
|
1315
|
+
}
|
|
1316
|
+
const allResults = memoriesWithEmbeddings.map((memory) => {
|
|
1317
|
+
const similarity = cosineSimilarity(queryEmbedding, memory.embedding);
|
|
1318
|
+
return {
|
|
1319
|
+
...memory,
|
|
1320
|
+
similarity
|
|
1321
|
+
};
|
|
1322
|
+
}).sort((a, b) => b.similarity - a.similarity);
|
|
1323
|
+
console.log(
|
|
1324
|
+
`[Memory Search] All similarity scores:`,
|
|
1325
|
+
allResults.map((r) => ({
|
|
1326
|
+
key: `${r.namespace}:${r.key}`,
|
|
1327
|
+
value: r.value,
|
|
1328
|
+
similarity: r.similarity.toFixed(4)
|
|
1329
|
+
}))
|
|
1330
|
+
);
|
|
1331
|
+
const results = allResults.filter((result) => result.similarity >= minSimilarity).slice(0, limit);
|
|
1332
|
+
if (results.length === 0 && allResults.length > 0) {
|
|
1333
|
+
const topSimilarity = allResults[0].similarity;
|
|
1334
|
+
const suggestedThreshold = Math.max(0.3, topSimilarity - 0.1);
|
|
1335
|
+
console.warn(
|
|
1336
|
+
`[Memory Search] No memories above threshold ${minSimilarity}. Highest similarity was ${topSimilarity.toFixed(4)}. Consider lowering the threshold to ${suggestedThreshold.toFixed(2)}`
|
|
1337
|
+
);
|
|
1338
|
+
} else {
|
|
1339
|
+
console.log(
|
|
1340
|
+
`[Memory Search] Found ${results.length} memories above similarity threshold ${minSimilarity}. Top similarity: ${results[0]?.similarity.toFixed(4) || "N/A"}`
|
|
1341
|
+
);
|
|
1342
|
+
}
|
|
1343
|
+
return results;
|
|
1090
1344
|
};
|
|
1091
|
-
|
|
1092
|
-
// src/clientConfig.ts
|
|
1093
|
-
var createClientConfig = (config) => ({
|
|
1094
|
-
...config,
|
|
1095
|
-
baseUrl: "https://ai-portal-dev.zetachain.com"
|
|
1096
|
-
});
|
|
1097
|
-
|
|
1098
|
-
// src/client/client.gen.ts
|
|
1099
|
-
var client = createClient(createClientConfig(createConfig()));
|
|
1100
1345
|
|
|
1101
1346
|
// src/client/sdk.gen.ts
|
|
1102
1347
|
var postApiV1Embeddings = (options) => {
|
|
@@ -1214,7 +1459,7 @@ function useMemory(options = {}) {
|
|
|
1214
1459
|
onFactsExtracted,
|
|
1215
1460
|
getToken
|
|
1216
1461
|
} = options;
|
|
1217
|
-
const extractionInProgressRef =
|
|
1462
|
+
const extractionInProgressRef = useRef3(false);
|
|
1218
1463
|
const extractMemoriesFromMessage = useCallback2(
|
|
1219
1464
|
async (options2) => {
|
|
1220
1465
|
const { messages, model } = options2;
|
|
@@ -1228,7 +1473,7 @@ function useMemory(options = {}) {
|
|
|
1228
1473
|
console.error("No access token available for memory extraction");
|
|
1229
1474
|
return null;
|
|
1230
1475
|
}
|
|
1231
|
-
const completion = await
|
|
1476
|
+
const completion = await postApiV1ChatCompletions({
|
|
1232
1477
|
body: {
|
|
1233
1478
|
messages: [
|
|
1234
1479
|
{
|
|
@@ -1485,8 +1730,12 @@ var extractConversationContext = (messages, maxMessages = 3) => {
|
|
|
1485
1730
|
};
|
|
1486
1731
|
export {
|
|
1487
1732
|
createMemoryContextSystemMessage,
|
|
1733
|
+
decryptData,
|
|
1734
|
+
decryptDataBytes,
|
|
1735
|
+
encryptData,
|
|
1488
1736
|
extractConversationContext,
|
|
1489
1737
|
formatMemoriesForChat,
|
|
1490
1738
|
useChat,
|
|
1739
|
+
useEncryption,
|
|
1491
1740
|
useMemory
|
|
1492
1741
|
};
|