@reverbia/sdk 1.0.0-next.20251212012743 → 1.0.0-next.20251215143604
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/expo/index.cjs +1661 -20
- package/dist/expo/index.d.mts +748 -1
- package/dist/expo/index.d.ts +748 -1
- package/dist/expo/index.mjs +1641 -10
- package/dist/next/index.d.mts +2 -0
- package/dist/next/index.d.ts +2 -0
- package/dist/react/index.cjs +1377 -418
- package/dist/react/index.d.mts +702 -47
- package/dist/react/index.d.ts +702 -47
- package/dist/react/index.mjs +1348 -398
- package/package.json +7 -6
package/dist/react/index.cjs
CHANGED
|
@@ -30,7 +30,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/react/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
ChatConversation: () => Conversation,
|
|
34
|
+
ChatMessage: () => Message,
|
|
33
35
|
DEFAULT_TOOL_SELECTOR_MODEL: () => DEFAULT_TOOL_SELECTOR_MODEL,
|
|
36
|
+
StoredMemoryModel: () => Memory,
|
|
37
|
+
chatStorageSchema: () => chatStorageSchema,
|
|
34
38
|
createMemoryContextSystemMessage: () => createMemoryContextSystemMessage,
|
|
35
39
|
decryptData: () => decryptData,
|
|
36
40
|
decryptDataBytes: () => decryptDataBytes,
|
|
@@ -38,13 +42,18 @@ __export(index_exports, {
|
|
|
38
42
|
executeTool: () => executeTool,
|
|
39
43
|
extractConversationContext: () => extractConversationContext,
|
|
40
44
|
formatMemoriesForChat: () => formatMemoriesForChat,
|
|
45
|
+
generateCompositeKey: () => generateCompositeKey,
|
|
46
|
+
generateConversationId: () => generateConversationId,
|
|
47
|
+
generateUniqueKey: () => generateUniqueKey,
|
|
41
48
|
hasEncryptionKey: () => hasEncryptionKey,
|
|
49
|
+
memoryStorageSchema: () => memoryStorageSchema,
|
|
42
50
|
requestEncryptionKey: () => requestEncryptionKey,
|
|
43
51
|
selectTool: () => selectTool,
|
|
44
52
|
useChat: () => useChat,
|
|
53
|
+
useChatStorage: () => useChatStorage,
|
|
45
54
|
useEncryption: () => useEncryption,
|
|
46
55
|
useImageGeneration: () => useImageGeneration,
|
|
47
|
-
|
|
56
|
+
useMemoryStorage: () => useMemoryStorage,
|
|
48
57
|
useModels: () => useModels,
|
|
49
58
|
useOCR: () => useOCR,
|
|
50
59
|
usePdf: () => usePdf,
|
|
@@ -1280,7 +1289,8 @@ function useChat(options) {
|
|
|
1280
1289
|
model,
|
|
1281
1290
|
onData,
|
|
1282
1291
|
runTools = true,
|
|
1283
|
-
headers
|
|
1292
|
+
headers,
|
|
1293
|
+
memoryContext
|
|
1284
1294
|
}) => {
|
|
1285
1295
|
const messagesValidation = validateMessages(messages);
|
|
1286
1296
|
if (!messagesValidation.valid) {
|
|
@@ -1293,7 +1303,20 @@ function useChat(options) {
|
|
|
1293
1303
|
abortControllerRef.current = abortController;
|
|
1294
1304
|
setIsLoading(true);
|
|
1295
1305
|
let toolExecutionResult;
|
|
1296
|
-
let
|
|
1306
|
+
let messagesWithContext = messages;
|
|
1307
|
+
if (memoryContext) {
|
|
1308
|
+
const memorySystemMessage = {
|
|
1309
|
+
role: "system",
|
|
1310
|
+
content: [
|
|
1311
|
+
{
|
|
1312
|
+
type: "text",
|
|
1313
|
+
text: memoryContext
|
|
1314
|
+
}
|
|
1315
|
+
]
|
|
1316
|
+
};
|
|
1317
|
+
messagesWithContext = [memorySystemMessage, ...messages];
|
|
1318
|
+
}
|
|
1319
|
+
let messagesWithToolContext = messagesWithContext;
|
|
1297
1320
|
const shouldRunTools = runTools && tools && tools.length > 0;
|
|
1298
1321
|
if (shouldRunTools) {
|
|
1299
1322
|
const lastUserMessage = [...messages].reverse().find((m) => m.role === "user");
|
|
@@ -1670,16 +1693,926 @@ async function requestEncryptionKey(walletAddress, signMessage) {
|
|
|
1670
1693
|
throw new Error("Failed to store encryption key in localStorage");
|
|
1671
1694
|
}
|
|
1672
1695
|
}
|
|
1673
|
-
function useEncryption(signMessage) {
|
|
1674
|
-
return {
|
|
1675
|
-
requestEncryptionKey: (walletAddress) => requestEncryptionKey(walletAddress, signMessage)
|
|
1676
|
-
};
|
|
1696
|
+
function useEncryption(signMessage) {
|
|
1697
|
+
return {
|
|
1698
|
+
requestEncryptionKey: (walletAddress) => requestEncryptionKey(walletAddress, signMessage)
|
|
1699
|
+
};
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
// src/react/useChatStorage.ts
|
|
1703
|
+
var import_react2 = require("react");
|
|
1704
|
+
|
|
1705
|
+
// src/lib/chatStorage/types.ts
|
|
1706
|
+
function convertUsageToStored(usage) {
|
|
1707
|
+
if (!usage) return void 0;
|
|
1708
|
+
return {
|
|
1709
|
+
promptTokens: usage.prompt_tokens,
|
|
1710
|
+
completionTokens: usage.completion_tokens,
|
|
1711
|
+
totalTokens: usage.total_tokens,
|
|
1712
|
+
costMicroUsd: usage.cost_micro_usd
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
function generateConversationId() {
|
|
1716
|
+
return `conv_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
// src/lib/chatStorage/operations.ts
|
|
1720
|
+
var import_watermelondb = require("@nozbe/watermelondb");
|
|
1721
|
+
function messageToStored(message) {
|
|
1722
|
+
return {
|
|
1723
|
+
uniqueId: message.id,
|
|
1724
|
+
messageId: message.messageId,
|
|
1725
|
+
conversationId: message.conversationId,
|
|
1726
|
+
role: message.role,
|
|
1727
|
+
content: message.content,
|
|
1728
|
+
model: message.model,
|
|
1729
|
+
files: message.files,
|
|
1730
|
+
createdAt: message.createdAt,
|
|
1731
|
+
updatedAt: message.updatedAt,
|
|
1732
|
+
vector: message.vector,
|
|
1733
|
+
embeddingModel: message.embeddingModel,
|
|
1734
|
+
usage: message.usage,
|
|
1735
|
+
sources: message.sources,
|
|
1736
|
+
responseDuration: message.responseDuration
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
function conversationToStored(conversation) {
|
|
1740
|
+
return {
|
|
1741
|
+
uniqueId: conversation.id,
|
|
1742
|
+
conversationId: conversation.conversationId,
|
|
1743
|
+
title: conversation.title,
|
|
1744
|
+
createdAt: conversation.createdAt,
|
|
1745
|
+
updatedAt: conversation.updatedAt,
|
|
1746
|
+
isDeleted: conversation.isDeleted
|
|
1747
|
+
};
|
|
1748
|
+
}
|
|
1749
|
+
async function createConversationOp(ctx, opts, defaultTitle = "New Conversation") {
|
|
1750
|
+
const convId = opts?.conversationId || generateConversationId();
|
|
1751
|
+
const title = opts?.title || defaultTitle;
|
|
1752
|
+
const created = await ctx.database.write(async () => {
|
|
1753
|
+
return await ctx.conversationsCollection.create((conv) => {
|
|
1754
|
+
conv._setRaw("conversation_id", convId);
|
|
1755
|
+
conv._setRaw("title", title);
|
|
1756
|
+
conv._setRaw("is_deleted", false);
|
|
1757
|
+
});
|
|
1758
|
+
});
|
|
1759
|
+
return conversationToStored(created);
|
|
1760
|
+
}
|
|
1761
|
+
async function getConversationOp(ctx, id) {
|
|
1762
|
+
const results = await ctx.conversationsCollection.query(import_watermelondb.Q.where("conversation_id", id), import_watermelondb.Q.where("is_deleted", false)).fetch();
|
|
1763
|
+
return results.length > 0 ? conversationToStored(results[0]) : null;
|
|
1764
|
+
}
|
|
1765
|
+
async function getConversationsOp(ctx) {
|
|
1766
|
+
const results = await ctx.conversationsCollection.query(import_watermelondb.Q.where("is_deleted", false), import_watermelondb.Q.sortBy("created_at", import_watermelondb.Q.desc)).fetch();
|
|
1767
|
+
return results.map(conversationToStored);
|
|
1768
|
+
}
|
|
1769
|
+
async function updateConversationTitleOp(ctx, id, title) {
|
|
1770
|
+
const results = await ctx.conversationsCollection.query(import_watermelondb.Q.where("conversation_id", id), import_watermelondb.Q.where("is_deleted", false)).fetch();
|
|
1771
|
+
if (results.length > 0) {
|
|
1772
|
+
await ctx.database.write(async () => {
|
|
1773
|
+
await results[0].update((conv) => {
|
|
1774
|
+
conv._setRaw("title", title);
|
|
1775
|
+
});
|
|
1776
|
+
});
|
|
1777
|
+
return true;
|
|
1778
|
+
}
|
|
1779
|
+
return false;
|
|
1780
|
+
}
|
|
1781
|
+
async function deleteConversationOp(ctx, id) {
|
|
1782
|
+
const results = await ctx.conversationsCollection.query(import_watermelondb.Q.where("conversation_id", id), import_watermelondb.Q.where("is_deleted", false)).fetch();
|
|
1783
|
+
if (results.length > 0) {
|
|
1784
|
+
await ctx.database.write(async () => {
|
|
1785
|
+
await results[0].update((conv) => {
|
|
1786
|
+
conv._setRaw("is_deleted", true);
|
|
1787
|
+
});
|
|
1788
|
+
});
|
|
1789
|
+
return true;
|
|
1790
|
+
}
|
|
1791
|
+
return false;
|
|
1792
|
+
}
|
|
1793
|
+
async function getMessagesOp(ctx, convId) {
|
|
1794
|
+
const results = await ctx.messagesCollection.query(import_watermelondb.Q.where("conversation_id", convId), import_watermelondb.Q.sortBy("message_id", import_watermelondb.Q.asc)).fetch();
|
|
1795
|
+
return results.map(messageToStored);
|
|
1796
|
+
}
|
|
1797
|
+
async function getMessageCountOp(ctx, convId) {
|
|
1798
|
+
return await ctx.messagesCollection.query(import_watermelondb.Q.where("conversation_id", convId)).fetchCount();
|
|
1799
|
+
}
|
|
1800
|
+
async function clearMessagesOp(ctx, convId) {
|
|
1801
|
+
const messages = await ctx.messagesCollection.query(import_watermelondb.Q.where("conversation_id", convId)).fetch();
|
|
1802
|
+
await ctx.database.write(async () => {
|
|
1803
|
+
for (const message of messages) {
|
|
1804
|
+
await message.destroyPermanently();
|
|
1805
|
+
}
|
|
1806
|
+
});
|
|
1807
|
+
}
|
|
1808
|
+
async function createMessageOp(ctx, opts) {
|
|
1809
|
+
const existingCount = await getMessageCountOp(ctx, opts.conversationId);
|
|
1810
|
+
const messageId = existingCount + 1;
|
|
1811
|
+
const created = await ctx.database.write(async () => {
|
|
1812
|
+
return await ctx.messagesCollection.create((msg) => {
|
|
1813
|
+
msg._setRaw("message_id", messageId);
|
|
1814
|
+
msg._setRaw("conversation_id", opts.conversationId);
|
|
1815
|
+
msg._setRaw("role", opts.role);
|
|
1816
|
+
msg._setRaw("content", opts.content);
|
|
1817
|
+
if (opts.model) msg._setRaw("model", opts.model);
|
|
1818
|
+
if (opts.files) msg._setRaw("files", JSON.stringify(opts.files));
|
|
1819
|
+
if (opts.usage) msg._setRaw("usage", JSON.stringify(opts.usage));
|
|
1820
|
+
if (opts.sources) msg._setRaw("sources", JSON.stringify(opts.sources));
|
|
1821
|
+
if (opts.responseDuration !== void 0)
|
|
1822
|
+
msg._setRaw("response_duration", opts.responseDuration);
|
|
1823
|
+
if (opts.vector) msg._setRaw("vector", JSON.stringify(opts.vector));
|
|
1824
|
+
if (opts.embeddingModel) msg._setRaw("embedding_model", opts.embeddingModel);
|
|
1825
|
+
});
|
|
1826
|
+
});
|
|
1827
|
+
return messageToStored(created);
|
|
1828
|
+
}
|
|
1829
|
+
async function updateMessageEmbeddingOp(ctx, uniqueId, vector, embeddingModel) {
|
|
1830
|
+
let message;
|
|
1831
|
+
try {
|
|
1832
|
+
message = await ctx.messagesCollection.find(uniqueId);
|
|
1833
|
+
} catch {
|
|
1834
|
+
return null;
|
|
1835
|
+
}
|
|
1836
|
+
await ctx.database.write(async () => {
|
|
1837
|
+
await message.update((msg) => {
|
|
1838
|
+
msg._setRaw("vector", JSON.stringify(vector));
|
|
1839
|
+
msg._setRaw("embedding_model", embeddingModel);
|
|
1840
|
+
});
|
|
1841
|
+
});
|
|
1842
|
+
return messageToStored(message);
|
|
1843
|
+
}
|
|
1844
|
+
function cosineSimilarity(a, b) {
|
|
1845
|
+
if (a.length !== b.length) return 0;
|
|
1846
|
+
let dotProduct = 0;
|
|
1847
|
+
let normA = 0;
|
|
1848
|
+
let normB = 0;
|
|
1849
|
+
for (let i = 0; i < a.length; i++) {
|
|
1850
|
+
dotProduct += a[i] * b[i];
|
|
1851
|
+
normA += a[i] * a[i];
|
|
1852
|
+
normB += b[i] * b[i];
|
|
1853
|
+
}
|
|
1854
|
+
const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
|
|
1855
|
+
return magnitude === 0 ? 0 : dotProduct / magnitude;
|
|
1856
|
+
}
|
|
1857
|
+
async function searchMessagesOp(ctx, queryVector, options) {
|
|
1858
|
+
const { limit = 10, minSimilarity = 0.5, conversationId } = options || {};
|
|
1859
|
+
const activeConversations = await ctx.conversationsCollection.query(import_watermelondb.Q.where("is_deleted", false)).fetch();
|
|
1860
|
+
const activeConversationIds = new Set(
|
|
1861
|
+
activeConversations.map((c) => c.conversationId)
|
|
1862
|
+
);
|
|
1863
|
+
const queryConditions = conversationId ? [import_watermelondb.Q.where("conversation_id", conversationId)] : [];
|
|
1864
|
+
const messages = await ctx.messagesCollection.query(...queryConditions).fetch();
|
|
1865
|
+
const resultsWithSimilarity = [];
|
|
1866
|
+
for (const message of messages) {
|
|
1867
|
+
if (!activeConversationIds.has(message.conversationId)) continue;
|
|
1868
|
+
const messageVector = message.vector;
|
|
1869
|
+
if (!messageVector || messageVector.length === 0) continue;
|
|
1870
|
+
const similarity = cosineSimilarity(queryVector, messageVector);
|
|
1871
|
+
if (similarity >= minSimilarity) {
|
|
1872
|
+
resultsWithSimilarity.push({
|
|
1873
|
+
...messageToStored(message),
|
|
1874
|
+
similarity
|
|
1875
|
+
});
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
return resultsWithSimilarity.sort((a, b) => b.similarity - a.similarity).slice(0, limit);
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
// src/react/useChatStorage.ts
|
|
1882
|
+
function storedToLlmapiMessage(stored) {
|
|
1883
|
+
const content = [
|
|
1884
|
+
{ type: "text", text: stored.content }
|
|
1885
|
+
];
|
|
1886
|
+
if (stored.files?.length) {
|
|
1887
|
+
for (const file of stored.files) {
|
|
1888
|
+
if (file.url) {
|
|
1889
|
+
content.push({
|
|
1890
|
+
type: "image_url",
|
|
1891
|
+
image_url: { url: file.url }
|
|
1892
|
+
});
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
return {
|
|
1897
|
+
role: stored.role,
|
|
1898
|
+
content
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1901
|
+
function useChatStorage(options) {
|
|
1902
|
+
const {
|
|
1903
|
+
database,
|
|
1904
|
+
conversationId: initialConversationId,
|
|
1905
|
+
autoCreateConversation = true,
|
|
1906
|
+
defaultConversationTitle = "New Conversation",
|
|
1907
|
+
getToken,
|
|
1908
|
+
baseUrl,
|
|
1909
|
+
onData,
|
|
1910
|
+
onFinish,
|
|
1911
|
+
onError,
|
|
1912
|
+
chatProvider,
|
|
1913
|
+
localModel,
|
|
1914
|
+
tools,
|
|
1915
|
+
toolSelectorModel,
|
|
1916
|
+
onToolExecution
|
|
1917
|
+
} = options;
|
|
1918
|
+
const [currentConversationId, setCurrentConversationId] = (0, import_react2.useState)(initialConversationId || null);
|
|
1919
|
+
const messagesCollection = (0, import_react2.useMemo)(
|
|
1920
|
+
() => database.get("history"),
|
|
1921
|
+
[database]
|
|
1922
|
+
);
|
|
1923
|
+
const conversationsCollection = (0, import_react2.useMemo)(
|
|
1924
|
+
() => database.get("conversations"),
|
|
1925
|
+
[database]
|
|
1926
|
+
);
|
|
1927
|
+
const storageCtx = (0, import_react2.useMemo)(
|
|
1928
|
+
() => ({
|
|
1929
|
+
database,
|
|
1930
|
+
messagesCollection,
|
|
1931
|
+
conversationsCollection
|
|
1932
|
+
}),
|
|
1933
|
+
[database, messagesCollection, conversationsCollection]
|
|
1934
|
+
);
|
|
1935
|
+
const {
|
|
1936
|
+
isLoading,
|
|
1937
|
+
isSelectingTool,
|
|
1938
|
+
sendMessage: baseSendMessage,
|
|
1939
|
+
stop
|
|
1940
|
+
} = useChat({
|
|
1941
|
+
getToken,
|
|
1942
|
+
baseUrl,
|
|
1943
|
+
onData,
|
|
1944
|
+
onFinish,
|
|
1945
|
+
onError,
|
|
1946
|
+
chatProvider,
|
|
1947
|
+
localModel,
|
|
1948
|
+
tools,
|
|
1949
|
+
toolSelectorModel,
|
|
1950
|
+
onToolExecution
|
|
1951
|
+
});
|
|
1952
|
+
const createConversation = (0, import_react2.useCallback)(
|
|
1953
|
+
async (opts) => {
|
|
1954
|
+
const created = await createConversationOp(
|
|
1955
|
+
storageCtx,
|
|
1956
|
+
opts,
|
|
1957
|
+
defaultConversationTitle
|
|
1958
|
+
);
|
|
1959
|
+
setCurrentConversationId(created.conversationId);
|
|
1960
|
+
return created;
|
|
1961
|
+
},
|
|
1962
|
+
[storageCtx, defaultConversationTitle]
|
|
1963
|
+
);
|
|
1964
|
+
const getConversation = (0, import_react2.useCallback)(
|
|
1965
|
+
async (id) => {
|
|
1966
|
+
return getConversationOp(storageCtx, id);
|
|
1967
|
+
},
|
|
1968
|
+
[storageCtx]
|
|
1969
|
+
);
|
|
1970
|
+
const getConversations = (0, import_react2.useCallback)(async () => {
|
|
1971
|
+
return getConversationsOp(storageCtx);
|
|
1972
|
+
}, [storageCtx]);
|
|
1973
|
+
const updateConversationTitle = (0, import_react2.useCallback)(
|
|
1974
|
+
async (id, title) => {
|
|
1975
|
+
return updateConversationTitleOp(storageCtx, id, title);
|
|
1976
|
+
},
|
|
1977
|
+
[storageCtx]
|
|
1978
|
+
);
|
|
1979
|
+
const deleteConversation = (0, import_react2.useCallback)(
|
|
1980
|
+
async (id) => {
|
|
1981
|
+
const deleted = await deleteConversationOp(storageCtx, id);
|
|
1982
|
+
if (deleted && currentConversationId === id) {
|
|
1983
|
+
setCurrentConversationId(null);
|
|
1984
|
+
}
|
|
1985
|
+
return deleted;
|
|
1986
|
+
},
|
|
1987
|
+
[storageCtx, currentConversationId]
|
|
1988
|
+
);
|
|
1989
|
+
const getMessages = (0, import_react2.useCallback)(
|
|
1990
|
+
async (convId) => {
|
|
1991
|
+
return getMessagesOp(storageCtx, convId);
|
|
1992
|
+
},
|
|
1993
|
+
[storageCtx]
|
|
1994
|
+
);
|
|
1995
|
+
const getMessageCount = (0, import_react2.useCallback)(
|
|
1996
|
+
async (convId) => {
|
|
1997
|
+
return getMessageCountOp(storageCtx, convId);
|
|
1998
|
+
},
|
|
1999
|
+
[storageCtx]
|
|
2000
|
+
);
|
|
2001
|
+
const clearMessages = (0, import_react2.useCallback)(
|
|
2002
|
+
async (convId) => {
|
|
2003
|
+
return clearMessagesOp(storageCtx, convId);
|
|
2004
|
+
},
|
|
2005
|
+
[storageCtx]
|
|
2006
|
+
);
|
|
2007
|
+
const ensureConversation = (0, import_react2.useCallback)(async () => {
|
|
2008
|
+
if (currentConversationId) {
|
|
2009
|
+
const existing = await getConversation(currentConversationId);
|
|
2010
|
+
if (existing) {
|
|
2011
|
+
return currentConversationId;
|
|
2012
|
+
}
|
|
2013
|
+
if (autoCreateConversation) {
|
|
2014
|
+
const newConv = await createConversation({
|
|
2015
|
+
conversationId: currentConversationId
|
|
2016
|
+
});
|
|
2017
|
+
return newConv.conversationId;
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
if (autoCreateConversation) {
|
|
2021
|
+
const newConv = await createConversation();
|
|
2022
|
+
return newConv.conversationId;
|
|
2023
|
+
}
|
|
2024
|
+
throw new Error(
|
|
2025
|
+
"No conversation ID provided and autoCreateConversation is disabled"
|
|
2026
|
+
);
|
|
2027
|
+
}, [
|
|
2028
|
+
currentConversationId,
|
|
2029
|
+
getConversation,
|
|
2030
|
+
autoCreateConversation,
|
|
2031
|
+
createConversation
|
|
2032
|
+
]);
|
|
2033
|
+
const sendMessage = (0, import_react2.useCallback)(
|
|
2034
|
+
async (args) => {
|
|
2035
|
+
const {
|
|
2036
|
+
content,
|
|
2037
|
+
model,
|
|
2038
|
+
messages: providedMessages,
|
|
2039
|
+
includeHistory = true,
|
|
2040
|
+
maxHistoryMessages = 50,
|
|
2041
|
+
files,
|
|
2042
|
+
onData: perRequestOnData,
|
|
2043
|
+
runTools,
|
|
2044
|
+
headers,
|
|
2045
|
+
memoryContext
|
|
2046
|
+
} = args;
|
|
2047
|
+
let convId;
|
|
2048
|
+
try {
|
|
2049
|
+
convId = await ensureConversation();
|
|
2050
|
+
} catch (err) {
|
|
2051
|
+
return {
|
|
2052
|
+
data: null,
|
|
2053
|
+
error: err instanceof Error ? err.message : "Failed to ensure conversation"
|
|
2054
|
+
};
|
|
2055
|
+
}
|
|
2056
|
+
let messagesToSend = [];
|
|
2057
|
+
if (includeHistory && !providedMessages) {
|
|
2058
|
+
const storedMessages = await getMessages(convId);
|
|
2059
|
+
const limitedMessages = storedMessages.slice(-maxHistoryMessages);
|
|
2060
|
+
messagesToSend = limitedMessages.map(storedToLlmapiMessage);
|
|
2061
|
+
} else if (providedMessages) {
|
|
2062
|
+
messagesToSend = providedMessages;
|
|
2063
|
+
}
|
|
2064
|
+
const userMessageContent = [
|
|
2065
|
+
{ type: "text", text: content }
|
|
2066
|
+
];
|
|
2067
|
+
if (files && files.length > 0) {
|
|
2068
|
+
for (const file of files) {
|
|
2069
|
+
if (file.url && file.type.startsWith("image/")) {
|
|
2070
|
+
userMessageContent.push({
|
|
2071
|
+
type: "image_url",
|
|
2072
|
+
image_url: { url: file.url }
|
|
2073
|
+
});
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
const userMessage = {
|
|
2078
|
+
role: "user",
|
|
2079
|
+
content: userMessageContent
|
|
2080
|
+
};
|
|
2081
|
+
messagesToSend.push(userMessage);
|
|
2082
|
+
const sanitizedFiles = files?.map((file) => ({
|
|
2083
|
+
id: file.id,
|
|
2084
|
+
name: file.name,
|
|
2085
|
+
type: file.type,
|
|
2086
|
+
size: file.size,
|
|
2087
|
+
// Only keep URL if it's not a data URI (e.g., external URLs)
|
|
2088
|
+
url: file.url && !file.url.startsWith("data:") ? file.url : void 0
|
|
2089
|
+
}));
|
|
2090
|
+
let storedUserMessage;
|
|
2091
|
+
try {
|
|
2092
|
+
storedUserMessage = await createMessageOp(storageCtx, {
|
|
2093
|
+
conversationId: convId,
|
|
2094
|
+
role: "user",
|
|
2095
|
+
content,
|
|
2096
|
+
files: sanitizedFiles
|
|
2097
|
+
});
|
|
2098
|
+
} catch (err) {
|
|
2099
|
+
return {
|
|
2100
|
+
data: null,
|
|
2101
|
+
error: err instanceof Error ? err.message : "Failed to store user message"
|
|
2102
|
+
};
|
|
2103
|
+
}
|
|
2104
|
+
const startTime = Date.now();
|
|
2105
|
+
const result = await baseSendMessage({
|
|
2106
|
+
messages: messagesToSend,
|
|
2107
|
+
model,
|
|
2108
|
+
onData: perRequestOnData,
|
|
2109
|
+
runTools,
|
|
2110
|
+
headers,
|
|
2111
|
+
memoryContext
|
|
2112
|
+
});
|
|
2113
|
+
const responseDuration = (Date.now() - startTime) / 1e3;
|
|
2114
|
+
if (result.error || !result.data) {
|
|
2115
|
+
return {
|
|
2116
|
+
data: null,
|
|
2117
|
+
error: result.error || "No response data received",
|
|
2118
|
+
toolExecution: result.toolExecution,
|
|
2119
|
+
userMessage: storedUserMessage
|
|
2120
|
+
};
|
|
2121
|
+
}
|
|
2122
|
+
const responseData = result.data;
|
|
2123
|
+
const assistantContent = responseData.choices?.[0]?.message?.content?.map((part) => part.text || "").join("") || "";
|
|
2124
|
+
let storedAssistantMessage;
|
|
2125
|
+
try {
|
|
2126
|
+
storedAssistantMessage = await createMessageOp(storageCtx, {
|
|
2127
|
+
conversationId: convId,
|
|
2128
|
+
role: "assistant",
|
|
2129
|
+
content: assistantContent,
|
|
2130
|
+
model: responseData.model,
|
|
2131
|
+
usage: convertUsageToStored(responseData.usage),
|
|
2132
|
+
responseDuration
|
|
2133
|
+
});
|
|
2134
|
+
} catch (err) {
|
|
2135
|
+
return {
|
|
2136
|
+
data: null,
|
|
2137
|
+
error: err instanceof Error ? err.message : "Failed to store assistant message",
|
|
2138
|
+
toolExecution: result.toolExecution,
|
|
2139
|
+
userMessage: storedUserMessage
|
|
2140
|
+
};
|
|
2141
|
+
}
|
|
2142
|
+
return {
|
|
2143
|
+
data: responseData,
|
|
2144
|
+
error: null,
|
|
2145
|
+
toolExecution: result.toolExecution,
|
|
2146
|
+
userMessage: storedUserMessage,
|
|
2147
|
+
assistantMessage: storedAssistantMessage
|
|
2148
|
+
};
|
|
2149
|
+
},
|
|
2150
|
+
[ensureConversation, getMessages, storageCtx, baseSendMessage]
|
|
2151
|
+
);
|
|
2152
|
+
const searchMessages = (0, import_react2.useCallback)(
|
|
2153
|
+
async (queryVector, options2) => {
|
|
2154
|
+
return searchMessagesOp(storageCtx, queryVector, options2);
|
|
2155
|
+
},
|
|
2156
|
+
[storageCtx]
|
|
2157
|
+
);
|
|
2158
|
+
const updateMessageEmbedding = (0, import_react2.useCallback)(
|
|
2159
|
+
async (uniqueId, vector, embeddingModel) => {
|
|
2160
|
+
return updateMessageEmbeddingOp(storageCtx, uniqueId, vector, embeddingModel);
|
|
2161
|
+
},
|
|
2162
|
+
[storageCtx]
|
|
2163
|
+
);
|
|
2164
|
+
return {
|
|
2165
|
+
isLoading,
|
|
2166
|
+
isSelectingTool,
|
|
2167
|
+
sendMessage,
|
|
2168
|
+
stop,
|
|
2169
|
+
conversationId: currentConversationId,
|
|
2170
|
+
setConversationId: setCurrentConversationId,
|
|
2171
|
+
createConversation,
|
|
2172
|
+
getConversation,
|
|
2173
|
+
getConversations,
|
|
2174
|
+
updateConversationTitle,
|
|
2175
|
+
deleteConversation,
|
|
2176
|
+
getMessages,
|
|
2177
|
+
getMessageCount,
|
|
2178
|
+
clearMessages,
|
|
2179
|
+
searchMessages,
|
|
2180
|
+
updateMessageEmbedding
|
|
2181
|
+
};
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2184
|
+
// src/lib/chatStorage/schema.ts
|
|
2185
|
+
var import_watermelondb2 = require("@nozbe/watermelondb");
|
|
2186
|
+
var chatStorageSchema = (0, import_watermelondb2.appSchema)({
|
|
2187
|
+
version: 1,
|
|
2188
|
+
tables: [
|
|
2189
|
+
(0, import_watermelondb2.tableSchema)({
|
|
2190
|
+
name: "history",
|
|
2191
|
+
columns: [
|
|
2192
|
+
{ name: "message_id", type: "number" },
|
|
2193
|
+
// Sequential ID within conversation
|
|
2194
|
+
{ name: "conversation_id", type: "string", isIndexed: true },
|
|
2195
|
+
{ name: "role", type: "string", isIndexed: true },
|
|
2196
|
+
// 'user' | 'assistant' | 'system'
|
|
2197
|
+
{ name: "content", type: "string" },
|
|
2198
|
+
{ name: "model", type: "string", isOptional: true },
|
|
2199
|
+
{ name: "files", type: "string", isOptional: true },
|
|
2200
|
+
// JSON stringified FileMetadata[]
|
|
2201
|
+
{ name: "created_at", type: "number", isIndexed: true },
|
|
2202
|
+
{ name: "updated_at", type: "number" },
|
|
2203
|
+
{ name: "vector", type: "string", isOptional: true },
|
|
2204
|
+
// JSON stringified number[]
|
|
2205
|
+
{ name: "embedding_model", type: "string", isOptional: true },
|
|
2206
|
+
{ name: "usage", type: "string", isOptional: true },
|
|
2207
|
+
// JSON stringified ChatCompletionUsage
|
|
2208
|
+
{ name: "sources", type: "string", isOptional: true },
|
|
2209
|
+
// JSON stringified SearchSource[]
|
|
2210
|
+
{ name: "response_duration", type: "number", isOptional: true }
|
|
2211
|
+
]
|
|
2212
|
+
}),
|
|
2213
|
+
(0, import_watermelondb2.tableSchema)({
|
|
2214
|
+
name: "conversations",
|
|
2215
|
+
columns: [
|
|
2216
|
+
{ name: "conversation_id", type: "string", isIndexed: true },
|
|
2217
|
+
{ name: "title", type: "string" },
|
|
2218
|
+
{ name: "created_at", type: "number" },
|
|
2219
|
+
{ name: "updated_at", type: "number" },
|
|
2220
|
+
{ name: "is_deleted", type: "boolean", isIndexed: true }
|
|
2221
|
+
]
|
|
2222
|
+
})
|
|
2223
|
+
]
|
|
2224
|
+
});
|
|
2225
|
+
|
|
2226
|
+
// src/lib/chatStorage/models.ts
|
|
2227
|
+
var import_watermelondb3 = require("@nozbe/watermelondb");
|
|
2228
|
+
var Message = class extends import_watermelondb3.Model {
|
|
2229
|
+
/** Sequential message ID within conversation */
|
|
2230
|
+
get messageId() {
|
|
2231
|
+
return this._getRaw("message_id");
|
|
2232
|
+
}
|
|
2233
|
+
/** Links message to its conversation */
|
|
2234
|
+
get conversationId() {
|
|
2235
|
+
return this._getRaw("conversation_id");
|
|
2236
|
+
}
|
|
2237
|
+
/** Who sent the message: 'user' | 'assistant' | 'system' */
|
|
2238
|
+
get role() {
|
|
2239
|
+
return this._getRaw("role");
|
|
2240
|
+
}
|
|
2241
|
+
/** The message text content */
|
|
2242
|
+
get content() {
|
|
2243
|
+
return this._getRaw("content");
|
|
2244
|
+
}
|
|
2245
|
+
/** LLM model used (e.g., GPT-4, Claude) */
|
|
2246
|
+
get model() {
|
|
2247
|
+
const value = this._getRaw("model");
|
|
2248
|
+
return value ? value : void 0;
|
|
2249
|
+
}
|
|
2250
|
+
/** Optional attached files */
|
|
2251
|
+
get files() {
|
|
2252
|
+
const raw = this._getRaw("files");
|
|
2253
|
+
if (!raw) return void 0;
|
|
2254
|
+
try {
|
|
2255
|
+
return JSON.parse(raw);
|
|
2256
|
+
} catch {
|
|
2257
|
+
return void 0;
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
/** Created timestamp */
|
|
2261
|
+
get createdAt() {
|
|
2262
|
+
return new Date(this._getRaw("created_at"));
|
|
2263
|
+
}
|
|
2264
|
+
/** Updated timestamp */
|
|
2265
|
+
get updatedAt() {
|
|
2266
|
+
return new Date(this._getRaw("updated_at"));
|
|
2267
|
+
}
|
|
2268
|
+
/** Embedding vector for semantic search */
|
|
2269
|
+
get vector() {
|
|
2270
|
+
const raw = this._getRaw("vector");
|
|
2271
|
+
if (!raw) return void 0;
|
|
2272
|
+
try {
|
|
2273
|
+
return JSON.parse(raw);
|
|
2274
|
+
} catch {
|
|
2275
|
+
return void 0;
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
/** Model used to generate embedding */
|
|
2279
|
+
get embeddingModel() {
|
|
2280
|
+
const value = this._getRaw("embedding_model");
|
|
2281
|
+
return value ? value : void 0;
|
|
2282
|
+
}
|
|
2283
|
+
/** Token counts and cost */
|
|
2284
|
+
get usage() {
|
|
2285
|
+
const raw = this._getRaw("usage");
|
|
2286
|
+
if (!raw) return void 0;
|
|
2287
|
+
try {
|
|
2288
|
+
return JSON.parse(raw);
|
|
2289
|
+
} catch {
|
|
2290
|
+
return void 0;
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
/** Web search sources */
|
|
2294
|
+
get sources() {
|
|
2295
|
+
const raw = this._getRaw("sources");
|
|
2296
|
+
if (!raw) return void 0;
|
|
2297
|
+
try {
|
|
2298
|
+
return JSON.parse(raw);
|
|
2299
|
+
} catch {
|
|
2300
|
+
return void 0;
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
/** Response time in seconds */
|
|
2304
|
+
get responseDuration() {
|
|
2305
|
+
const value = this._getRaw("response_duration");
|
|
2306
|
+
return value !== null && value !== void 0 ? value : void 0;
|
|
2307
|
+
}
|
|
2308
|
+
};
|
|
2309
|
+
Message.table = "history";
|
|
2310
|
+
Message.associations = {
|
|
2311
|
+
conversations: { type: "belongs_to", key: "conversation_id" }
|
|
2312
|
+
};
|
|
2313
|
+
var Conversation = class extends import_watermelondb3.Model {
|
|
2314
|
+
/** Unique conversation identifier */
|
|
2315
|
+
get conversationId() {
|
|
2316
|
+
return this._getRaw("conversation_id");
|
|
2317
|
+
}
|
|
2318
|
+
/** Conversation title */
|
|
2319
|
+
get title() {
|
|
2320
|
+
return this._getRaw("title");
|
|
2321
|
+
}
|
|
2322
|
+
/** Created timestamp */
|
|
2323
|
+
get createdAt() {
|
|
2324
|
+
return new Date(this._getRaw("created_at"));
|
|
2325
|
+
}
|
|
2326
|
+
/** Updated timestamp */
|
|
2327
|
+
get updatedAt() {
|
|
2328
|
+
return new Date(this._getRaw("updated_at"));
|
|
2329
|
+
}
|
|
2330
|
+
/** Soft delete flag */
|
|
2331
|
+
get isDeleted() {
|
|
2332
|
+
return this._getRaw("is_deleted");
|
|
2333
|
+
}
|
|
2334
|
+
};
|
|
2335
|
+
Conversation.table = "conversations";
|
|
2336
|
+
Conversation.associations = {
|
|
2337
|
+
history: { type: "has_many", foreignKey: "conversation_id" }
|
|
2338
|
+
};
|
|
2339
|
+
|
|
2340
|
+
// src/react/useMemoryStorage.ts
|
|
2341
|
+
var import_react3 = require("react");
|
|
2342
|
+
var import_client6 = require("@reverbia/sdk");
|
|
2343
|
+
|
|
2344
|
+
// src/lib/memoryStorage/operations.ts
|
|
2345
|
+
var import_watermelondb4 = require("@nozbe/watermelondb");
|
|
2346
|
+
|
|
2347
|
+
// src/lib/memoryStorage/types.ts
|
|
2348
|
+
function generateCompositeKey(namespace, key) {
|
|
2349
|
+
return `${namespace}:${key}`;
|
|
2350
|
+
}
|
|
2351
|
+
function generateUniqueKey(namespace, key, value) {
|
|
2352
|
+
return `${namespace}:${key}:${value}`;
|
|
2353
|
+
}
|
|
2354
|
+
function cosineSimilarity2(a, b) {
|
|
2355
|
+
if (a.length !== b.length) {
|
|
2356
|
+
throw new Error("Vectors must have the same length");
|
|
2357
|
+
}
|
|
2358
|
+
let dotProduct = 0;
|
|
2359
|
+
let normA = 0;
|
|
2360
|
+
let normB = 0;
|
|
2361
|
+
for (let i = 0; i < a.length; i++) {
|
|
2362
|
+
dotProduct += a[i] * b[i];
|
|
2363
|
+
normA += a[i] * a[i];
|
|
2364
|
+
normB += b[i] * b[i];
|
|
2365
|
+
}
|
|
2366
|
+
const denominator = Math.sqrt(normA) * Math.sqrt(normB);
|
|
2367
|
+
if (denominator === 0) {
|
|
2368
|
+
return 0;
|
|
2369
|
+
}
|
|
2370
|
+
return dotProduct / denominator;
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
// src/lib/memoryStorage/operations.ts
|
|
2374
|
+
function memoryToStored(memory) {
|
|
2375
|
+
return {
|
|
2376
|
+
uniqueId: memory.id,
|
|
2377
|
+
type: memory.type,
|
|
2378
|
+
namespace: memory.namespace,
|
|
2379
|
+
key: memory.key,
|
|
2380
|
+
value: memory.value,
|
|
2381
|
+
rawEvidence: memory.rawEvidence,
|
|
2382
|
+
confidence: memory.confidence,
|
|
2383
|
+
pii: memory.pii,
|
|
2384
|
+
compositeKey: memory.compositeKey,
|
|
2385
|
+
uniqueKey: memory.uniqueKey,
|
|
2386
|
+
createdAt: memory.createdAt,
|
|
2387
|
+
updatedAt: memory.updatedAt,
|
|
2388
|
+
embedding: memory.embedding,
|
|
2389
|
+
embeddingModel: memory.embeddingModel,
|
|
2390
|
+
isDeleted: memory.isDeleted
|
|
2391
|
+
};
|
|
2392
|
+
}
|
|
2393
|
+
async function getAllMemoriesOp(ctx) {
|
|
2394
|
+
const results = await ctx.memoriesCollection.query(import_watermelondb4.Q.where("is_deleted", false), import_watermelondb4.Q.sortBy("created_at", import_watermelondb4.Q.desc)).fetch();
|
|
2395
|
+
return results.map(memoryToStored);
|
|
2396
|
+
}
|
|
2397
|
+
async function getMemoryByIdOp(ctx, id) {
|
|
2398
|
+
try {
|
|
2399
|
+
const memory = await ctx.memoriesCollection.find(id);
|
|
2400
|
+
if (memory.isDeleted) return null;
|
|
2401
|
+
return memoryToStored(memory);
|
|
2402
|
+
} catch {
|
|
2403
|
+
return null;
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
async function getMemoriesByNamespaceOp(ctx, namespace) {
|
|
2407
|
+
const results = await ctx.memoriesCollection.query(
|
|
2408
|
+
import_watermelondb4.Q.where("namespace", namespace),
|
|
2409
|
+
import_watermelondb4.Q.where("is_deleted", false),
|
|
2410
|
+
import_watermelondb4.Q.sortBy("created_at", import_watermelondb4.Q.desc)
|
|
2411
|
+
).fetch();
|
|
2412
|
+
return results.map(memoryToStored);
|
|
2413
|
+
}
|
|
2414
|
+
async function getMemoriesByKeyOp(ctx, namespace, key) {
|
|
2415
|
+
const compositeKey = generateCompositeKey(namespace, key);
|
|
2416
|
+
const results = await ctx.memoriesCollection.query(
|
|
2417
|
+
import_watermelondb4.Q.where("composite_key", compositeKey),
|
|
2418
|
+
import_watermelondb4.Q.where("is_deleted", false),
|
|
2419
|
+
import_watermelondb4.Q.sortBy("created_at", import_watermelondb4.Q.desc)
|
|
2420
|
+
).fetch();
|
|
2421
|
+
return results.map(memoryToStored);
|
|
2422
|
+
}
|
|
2423
|
+
async function saveMemoryOp(ctx, opts) {
|
|
2424
|
+
const compositeKey = generateCompositeKey(opts.namespace, opts.key);
|
|
2425
|
+
const uniqueKey = generateUniqueKey(opts.namespace, opts.key, opts.value);
|
|
2426
|
+
const result = await ctx.database.write(async () => {
|
|
2427
|
+
const existing = await ctx.memoriesCollection.query(import_watermelondb4.Q.where("unique_key", uniqueKey)).fetch();
|
|
2428
|
+
if (existing.length > 0) {
|
|
2429
|
+
const existingMemory = existing[0];
|
|
2430
|
+
const shouldPreserveEmbedding = existingMemory.value === opts.value && existingMemory.rawEvidence === opts.rawEvidence && existingMemory.type === opts.type && existingMemory.namespace === opts.namespace && existingMemory.key === opts.key && existingMemory.embedding !== void 0 && existingMemory.embedding.length > 0 && !opts.embedding;
|
|
2431
|
+
await existingMemory.update((mem) => {
|
|
2432
|
+
mem._setRaw("type", opts.type);
|
|
2433
|
+
mem._setRaw("namespace", opts.namespace);
|
|
2434
|
+
mem._setRaw("key", opts.key);
|
|
2435
|
+
mem._setRaw("value", opts.value);
|
|
2436
|
+
mem._setRaw("raw_evidence", opts.rawEvidence);
|
|
2437
|
+
mem._setRaw("confidence", opts.confidence);
|
|
2438
|
+
mem._setRaw("pii", opts.pii);
|
|
2439
|
+
mem._setRaw("composite_key", compositeKey);
|
|
2440
|
+
mem._setRaw("unique_key", uniqueKey);
|
|
2441
|
+
mem._setRaw("is_deleted", false);
|
|
2442
|
+
if (shouldPreserveEmbedding) {
|
|
2443
|
+
} else if (opts.embedding) {
|
|
2444
|
+
mem._setRaw("embedding", JSON.stringify(opts.embedding));
|
|
2445
|
+
if (opts.embeddingModel) {
|
|
2446
|
+
mem._setRaw("embedding_model", opts.embeddingModel);
|
|
2447
|
+
}
|
|
2448
|
+
} else {
|
|
2449
|
+
mem._setRaw("embedding", null);
|
|
2450
|
+
mem._setRaw("embedding_model", null);
|
|
2451
|
+
}
|
|
2452
|
+
});
|
|
2453
|
+
return existingMemory;
|
|
2454
|
+
}
|
|
2455
|
+
return await ctx.memoriesCollection.create((mem) => {
|
|
2456
|
+
mem._setRaw("type", opts.type);
|
|
2457
|
+
mem._setRaw("namespace", opts.namespace);
|
|
2458
|
+
mem._setRaw("key", opts.key);
|
|
2459
|
+
mem._setRaw("value", opts.value);
|
|
2460
|
+
mem._setRaw("raw_evidence", opts.rawEvidence);
|
|
2461
|
+
mem._setRaw("confidence", opts.confidence);
|
|
2462
|
+
mem._setRaw("pii", opts.pii);
|
|
2463
|
+
mem._setRaw("composite_key", compositeKey);
|
|
2464
|
+
mem._setRaw("unique_key", uniqueKey);
|
|
2465
|
+
mem._setRaw("is_deleted", false);
|
|
2466
|
+
if (opts.embedding) {
|
|
2467
|
+
mem._setRaw("embedding", JSON.stringify(opts.embedding));
|
|
2468
|
+
if (opts.embeddingModel) {
|
|
2469
|
+
mem._setRaw("embedding_model", opts.embeddingModel);
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
});
|
|
2473
|
+
});
|
|
2474
|
+
return memoryToStored(result);
|
|
2475
|
+
}
|
|
2476
|
+
async function saveMemoriesOp(ctx, memories) {
|
|
2477
|
+
const results = [];
|
|
2478
|
+
for (const memory of memories) {
|
|
2479
|
+
const saved = await saveMemoryOp(ctx, memory);
|
|
2480
|
+
results.push(saved);
|
|
2481
|
+
}
|
|
2482
|
+
return results;
|
|
2483
|
+
}
|
|
2484
|
+
async function updateMemoryOp(ctx, id, updates) {
|
|
2485
|
+
let memory;
|
|
2486
|
+
try {
|
|
2487
|
+
memory = await ctx.memoriesCollection.find(id);
|
|
2488
|
+
} catch {
|
|
2489
|
+
return { ok: false, reason: "not_found" };
|
|
2490
|
+
}
|
|
2491
|
+
if (memory.isDeleted) {
|
|
2492
|
+
return { ok: false, reason: "not_found" };
|
|
2493
|
+
}
|
|
2494
|
+
const newNamespace = updates.namespace ?? memory.namespace;
|
|
2495
|
+
const newKey = updates.key ?? memory.key;
|
|
2496
|
+
const newValue = updates.value ?? memory.value;
|
|
2497
|
+
const newCompositeKey = generateCompositeKey(newNamespace, newKey);
|
|
2498
|
+
const newUniqueKey = generateUniqueKey(newNamespace, newKey, newValue);
|
|
2499
|
+
if (newUniqueKey !== memory.uniqueKey) {
|
|
2500
|
+
const existing = await ctx.memoriesCollection.query(import_watermelondb4.Q.where("unique_key", newUniqueKey), import_watermelondb4.Q.where("is_deleted", false)).fetch();
|
|
2501
|
+
if (existing.length > 0) {
|
|
2502
|
+
return { ok: false, reason: "conflict", conflictingKey: newUniqueKey };
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
try {
|
|
2506
|
+
const updated = await ctx.database.write(async () => {
|
|
2507
|
+
await memory.update((mem) => {
|
|
2508
|
+
if (updates.type !== void 0) mem._setRaw("type", updates.type);
|
|
2509
|
+
if (updates.namespace !== void 0)
|
|
2510
|
+
mem._setRaw("namespace", updates.namespace);
|
|
2511
|
+
if (updates.key !== void 0) mem._setRaw("key", updates.key);
|
|
2512
|
+
if (updates.value !== void 0) mem._setRaw("value", updates.value);
|
|
2513
|
+
if (updates.rawEvidence !== void 0)
|
|
2514
|
+
mem._setRaw("raw_evidence", updates.rawEvidence);
|
|
2515
|
+
if (updates.confidence !== void 0)
|
|
2516
|
+
mem._setRaw("confidence", updates.confidence);
|
|
2517
|
+
if (updates.pii !== void 0) mem._setRaw("pii", updates.pii);
|
|
2518
|
+
if (updates.namespace !== void 0 || updates.key !== void 0 || updates.value !== void 0) {
|
|
2519
|
+
mem._setRaw("composite_key", newCompositeKey);
|
|
2520
|
+
mem._setRaw("unique_key", newUniqueKey);
|
|
2521
|
+
}
|
|
2522
|
+
if (updates.embedding !== void 0) {
|
|
2523
|
+
mem._setRaw(
|
|
2524
|
+
"embedding",
|
|
2525
|
+
updates.embedding ? JSON.stringify(updates.embedding) : null
|
|
2526
|
+
);
|
|
2527
|
+
}
|
|
2528
|
+
if (updates.embeddingModel !== void 0) {
|
|
2529
|
+
mem._setRaw("embedding_model", updates.embeddingModel || null);
|
|
2530
|
+
}
|
|
2531
|
+
});
|
|
2532
|
+
return memory;
|
|
2533
|
+
});
|
|
2534
|
+
return { ok: true, memory: memoryToStored(updated) };
|
|
2535
|
+
} catch (err) {
|
|
2536
|
+
return {
|
|
2537
|
+
ok: false,
|
|
2538
|
+
reason: "error",
|
|
2539
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
2540
|
+
};
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
async function deleteMemoryByIdOp(ctx, id) {
|
|
2544
|
+
try {
|
|
2545
|
+
const memory = await ctx.memoriesCollection.find(id);
|
|
2546
|
+
await ctx.database.write(async () => {
|
|
2547
|
+
await memory.update((mem) => {
|
|
2548
|
+
mem._setRaw("is_deleted", true);
|
|
2549
|
+
});
|
|
2550
|
+
});
|
|
2551
|
+
} catch {
|
|
2552
|
+
}
|
|
2553
|
+
}
|
|
2554
|
+
async function deleteMemoryOp(ctx, namespace, key, value) {
|
|
2555
|
+
const uniqueKey = generateUniqueKey(namespace, key, value);
|
|
2556
|
+
const results = await ctx.memoriesCollection.query(import_watermelondb4.Q.where("unique_key", uniqueKey)).fetch();
|
|
2557
|
+
if (results.length > 0) {
|
|
2558
|
+
await ctx.database.write(async () => {
|
|
2559
|
+
await results[0].update((mem) => {
|
|
2560
|
+
mem._setRaw("is_deleted", true);
|
|
2561
|
+
});
|
|
2562
|
+
});
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
async function deleteMemoriesByKeyOp(ctx, namespace, key) {
|
|
2566
|
+
const compositeKey = generateCompositeKey(namespace, key);
|
|
2567
|
+
const results = await ctx.memoriesCollection.query(import_watermelondb4.Q.where("composite_key", compositeKey), import_watermelondb4.Q.where("is_deleted", false)).fetch();
|
|
2568
|
+
await ctx.database.write(async () => {
|
|
2569
|
+
for (const memory of results) {
|
|
2570
|
+
await memory.update((mem) => {
|
|
2571
|
+
mem._setRaw("is_deleted", true);
|
|
2572
|
+
});
|
|
2573
|
+
}
|
|
2574
|
+
});
|
|
2575
|
+
}
|
|
2576
|
+
async function clearAllMemoriesOp(ctx) {
|
|
2577
|
+
const results = await ctx.memoriesCollection.query(import_watermelondb4.Q.where("is_deleted", false)).fetch();
|
|
2578
|
+
await ctx.database.write(async () => {
|
|
2579
|
+
for (const memory of results) {
|
|
2580
|
+
await memory.update((mem) => {
|
|
2581
|
+
mem._setRaw("is_deleted", true);
|
|
2582
|
+
});
|
|
2583
|
+
}
|
|
2584
|
+
});
|
|
2585
|
+
}
|
|
2586
|
+
async function searchSimilarMemoriesOp(ctx, queryEmbedding, limit = 10, minSimilarity = 0.6) {
|
|
2587
|
+
const allMemories = await ctx.memoriesCollection.query(import_watermelondb4.Q.where("is_deleted", false)).fetch();
|
|
2588
|
+
const memoriesWithEmbeddings = allMemories.filter(
|
|
2589
|
+
(m) => m.embedding && m.embedding.length > 0
|
|
2590
|
+
);
|
|
2591
|
+
if (memoriesWithEmbeddings.length === 0) {
|
|
2592
|
+
return [];
|
|
2593
|
+
}
|
|
2594
|
+
const results = memoriesWithEmbeddings.map((memory) => {
|
|
2595
|
+
const similarity = cosineSimilarity2(queryEmbedding, memory.embedding);
|
|
2596
|
+
return {
|
|
2597
|
+
...memoryToStored(memory),
|
|
2598
|
+
similarity
|
|
2599
|
+
};
|
|
2600
|
+
}).filter((result) => result.similarity >= minSimilarity).sort((a, b) => b.similarity - a.similarity).slice(0, limit);
|
|
2601
|
+
return results;
|
|
2602
|
+
}
|
|
2603
|
+
async function updateMemoryEmbeddingOp(ctx, id, embedding, embeddingModel) {
|
|
2604
|
+
try {
|
|
2605
|
+
const memory = await ctx.memoriesCollection.find(id);
|
|
2606
|
+
await ctx.database.write(async () => {
|
|
2607
|
+
await memory.update((mem) => {
|
|
2608
|
+
mem._setRaw("embedding", JSON.stringify(embedding));
|
|
2609
|
+
mem._setRaw("embedding_model", embeddingModel);
|
|
2610
|
+
});
|
|
2611
|
+
});
|
|
2612
|
+
} catch {
|
|
2613
|
+
}
|
|
1677
2614
|
}
|
|
1678
2615
|
|
|
1679
|
-
// src/react/useMemory.ts
|
|
1680
|
-
var import_react2 = require("react");
|
|
1681
|
-
var import_client6 = require("@reverbia/sdk");
|
|
1682
|
-
|
|
1683
2616
|
// src/lib/memory/service.ts
|
|
1684
2617
|
var FACT_EXTRACTION_PROMPT = `You are a memory extraction system. Extract durable user memories from chat messages.
|
|
1685
2618
|
|
|
@@ -1809,180 +2742,6 @@ var preprocessMemories = (items, minConfidence = 0.6) => {
|
|
|
1809
2742
|
return Array.from(deduplicatedMap.values());
|
|
1810
2743
|
};
|
|
1811
2744
|
|
|
1812
|
-
// src/lib/memory/db.ts
|
|
1813
|
-
var import_dexie = __toESM(require("dexie"));
|
|
1814
|
-
var MemoryDatabase = class extends import_dexie.default {
|
|
1815
|
-
constructor() {
|
|
1816
|
-
super("MemoryDatabase");
|
|
1817
|
-
this.version(2).stores({
|
|
1818
|
-
memories: "++id, uniqueKey, compositeKey, namespace, key, type, createdAt, updatedAt"
|
|
1819
|
-
});
|
|
1820
|
-
this.version(3).stores({
|
|
1821
|
-
memories: "++id, uniqueKey, compositeKey, namespace, key, type, createdAt, updatedAt"
|
|
1822
|
-
});
|
|
1823
|
-
}
|
|
1824
|
-
};
|
|
1825
|
-
var memoryDb = new MemoryDatabase();
|
|
1826
|
-
var saveMemory = async (memory) => {
|
|
1827
|
-
const compositeKey = `${memory.namespace}:${memory.key}`;
|
|
1828
|
-
const uniqueKey = `${memory.namespace}:${memory.key}:${memory.value}`;
|
|
1829
|
-
const now = Date.now();
|
|
1830
|
-
const existing = await memoryDb.memories.where("uniqueKey").equals(uniqueKey).first();
|
|
1831
|
-
if (existing) {
|
|
1832
|
-
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;
|
|
1833
|
-
const updateData = {
|
|
1834
|
-
...memory,
|
|
1835
|
-
compositeKey,
|
|
1836
|
-
uniqueKey,
|
|
1837
|
-
updatedAt: now,
|
|
1838
|
-
createdAt: existing.createdAt
|
|
1839
|
-
};
|
|
1840
|
-
if (shouldPreserveEmbedding) {
|
|
1841
|
-
updateData.embedding = existing.embedding;
|
|
1842
|
-
updateData.embeddingModel = existing.embeddingModel;
|
|
1843
|
-
} else {
|
|
1844
|
-
updateData.embedding = [];
|
|
1845
|
-
updateData.embeddingModel = void 0;
|
|
1846
|
-
}
|
|
1847
|
-
await memoryDb.memories.update(existing.id, updateData);
|
|
1848
|
-
} else {
|
|
1849
|
-
await memoryDb.memories.add({
|
|
1850
|
-
...memory,
|
|
1851
|
-
compositeKey,
|
|
1852
|
-
uniqueKey,
|
|
1853
|
-
createdAt: now,
|
|
1854
|
-
updatedAt: now
|
|
1855
|
-
});
|
|
1856
|
-
}
|
|
1857
|
-
};
|
|
1858
|
-
var saveMemories = async (memories) => {
|
|
1859
|
-
await Promise.all(memories.map((memory) => saveMemory(memory)));
|
|
1860
|
-
};
|
|
1861
|
-
var updateMemoryById = async (id, updates, existingMemory, embedding, embeddingModel) => {
|
|
1862
|
-
const now = Date.now();
|
|
1863
|
-
const updatedMemory = {
|
|
1864
|
-
...updates,
|
|
1865
|
-
updatedAt: now
|
|
1866
|
-
};
|
|
1867
|
-
if ("namespace" in updates || "key" in updates || "value" in updates) {
|
|
1868
|
-
const namespace = updates.namespace ?? existingMemory.namespace;
|
|
1869
|
-
const key = updates.key ?? existingMemory.key;
|
|
1870
|
-
const value = updates.value ?? existingMemory.value;
|
|
1871
|
-
const newUniqueKey = `${namespace}:${key}:${value}`;
|
|
1872
|
-
if (newUniqueKey !== existingMemory.uniqueKey) {
|
|
1873
|
-
const conflicting = await getMemory(namespace, key, value);
|
|
1874
|
-
if (conflicting) {
|
|
1875
|
-
throw new Error(
|
|
1876
|
-
`A memory with uniqueKey "${newUniqueKey}" already exists (id: ${conflicting.id})`
|
|
1877
|
-
);
|
|
1878
|
-
}
|
|
1879
|
-
}
|
|
1880
|
-
updatedMemory.compositeKey = `${namespace}:${key}`;
|
|
1881
|
-
updatedMemory.uniqueKey = newUniqueKey;
|
|
1882
|
-
}
|
|
1883
|
-
updatedMemory.embedding = embedding;
|
|
1884
|
-
updatedMemory.embeddingModel = embeddingModel;
|
|
1885
|
-
return await memoryDb.transaction("rw", memoryDb.memories, async () => {
|
|
1886
|
-
await memoryDb.memories.update(id, updatedMemory);
|
|
1887
|
-
return await memoryDb.memories.get(id);
|
|
1888
|
-
});
|
|
1889
|
-
};
|
|
1890
|
-
var getAllMemories = async () => {
|
|
1891
|
-
return memoryDb.memories.toArray();
|
|
1892
|
-
};
|
|
1893
|
-
var getMemoryById = async (id) => {
|
|
1894
|
-
return memoryDb.memories.get(id);
|
|
1895
|
-
};
|
|
1896
|
-
var getMemoriesByNamespace = async (namespace) => {
|
|
1897
|
-
return memoryDb.memories.where("namespace").equals(namespace).toArray();
|
|
1898
|
-
};
|
|
1899
|
-
var getMemories = async (namespace, key) => {
|
|
1900
|
-
const compositeKey = `${namespace}:${key}`;
|
|
1901
|
-
return memoryDb.memories.where("compositeKey").equals(compositeKey).toArray();
|
|
1902
|
-
};
|
|
1903
|
-
var getMemory = async (namespace, key, value) => {
|
|
1904
|
-
const uniqueKey = `${namespace}:${key}:${value}`;
|
|
1905
|
-
return memoryDb.memories.where("uniqueKey").equals(uniqueKey).first();
|
|
1906
|
-
};
|
|
1907
|
-
var deleteMemories = async (namespace, key) => {
|
|
1908
|
-
const compositeKey = `${namespace}:${key}`;
|
|
1909
|
-
await memoryDb.memories.where("compositeKey").equals(compositeKey).delete();
|
|
1910
|
-
};
|
|
1911
|
-
var deleteMemory = async (namespace, key, value) => {
|
|
1912
|
-
const uniqueKey = `${namespace}:${key}:${value}`;
|
|
1913
|
-
const existing = await memoryDb.memories.where("uniqueKey").equals(uniqueKey).first();
|
|
1914
|
-
if (existing?.id) {
|
|
1915
|
-
await memoryDb.memories.delete(existing.id);
|
|
1916
|
-
}
|
|
1917
|
-
};
|
|
1918
|
-
var deleteMemoryById = async (id) => {
|
|
1919
|
-
await memoryDb.memories.delete(id);
|
|
1920
|
-
};
|
|
1921
|
-
var clearAllMemories = async () => {
|
|
1922
|
-
await memoryDb.memories.clear();
|
|
1923
|
-
};
|
|
1924
|
-
var cosineSimilarity = (a, b) => {
|
|
1925
|
-
if (a.length !== b.length) {
|
|
1926
|
-
throw new Error("Vectors must have the same length");
|
|
1927
|
-
}
|
|
1928
|
-
let dotProduct = 0;
|
|
1929
|
-
let normA = 0;
|
|
1930
|
-
let normB = 0;
|
|
1931
|
-
for (let i = 0; i < a.length; i++) {
|
|
1932
|
-
dotProduct += a[i] * b[i];
|
|
1933
|
-
normA += a[i] * a[i];
|
|
1934
|
-
normB += b[i] * b[i];
|
|
1935
|
-
}
|
|
1936
|
-
const denominator = Math.sqrt(normA) * Math.sqrt(normB);
|
|
1937
|
-
if (denominator === 0) {
|
|
1938
|
-
return 0;
|
|
1939
|
-
}
|
|
1940
|
-
return dotProduct / denominator;
|
|
1941
|
-
};
|
|
1942
|
-
var searchSimilarMemories = async (queryEmbedding, limit = 10, minSimilarity = 0.6) => {
|
|
1943
|
-
const allMemories = await getAllMemories();
|
|
1944
|
-
const memoriesWithEmbeddings = allMemories.filter(
|
|
1945
|
-
(m) => m.embedding && m.embedding.length > 0
|
|
1946
|
-
);
|
|
1947
|
-
console.log(
|
|
1948
|
-
`[Memory Search] Total memories: ${allMemories.length}, memories with embeddings: ${memoriesWithEmbeddings.length}`
|
|
1949
|
-
);
|
|
1950
|
-
if (memoriesWithEmbeddings.length === 0) {
|
|
1951
|
-
console.warn(
|
|
1952
|
-
"[Memory Search] No memories with embeddings found. Memories may need embeddings generated. Use generateAndStoreEmbeddings() to generate embeddings for existing memories."
|
|
1953
|
-
);
|
|
1954
|
-
return [];
|
|
1955
|
-
}
|
|
1956
|
-
const allResults = memoriesWithEmbeddings.map((memory) => {
|
|
1957
|
-
const similarity = cosineSimilarity(queryEmbedding, memory.embedding);
|
|
1958
|
-
return {
|
|
1959
|
-
...memory,
|
|
1960
|
-
similarity
|
|
1961
|
-
};
|
|
1962
|
-
}).sort((a, b) => b.similarity - a.similarity);
|
|
1963
|
-
console.log(
|
|
1964
|
-
`[Memory Search] All similarity scores:`,
|
|
1965
|
-
allResults.map((r) => ({
|
|
1966
|
-
key: `${r.namespace}:${r.key}`,
|
|
1967
|
-
value: r.value,
|
|
1968
|
-
similarity: r.similarity.toFixed(4)
|
|
1969
|
-
}))
|
|
1970
|
-
);
|
|
1971
|
-
const results = allResults.filter((result) => result.similarity >= minSimilarity).slice(0, limit);
|
|
1972
|
-
if (results.length === 0 && allResults.length > 0) {
|
|
1973
|
-
const topSimilarity = allResults[0].similarity;
|
|
1974
|
-
const suggestedThreshold = Math.max(0.3, topSimilarity - 0.1);
|
|
1975
|
-
console.warn(
|
|
1976
|
-
`[Memory Search] No memories above threshold ${minSimilarity}. Highest similarity was ${topSimilarity.toFixed(4)}. Consider lowering the threshold to ${suggestedThreshold.toFixed(2)}`
|
|
1977
|
-
);
|
|
1978
|
-
} else {
|
|
1979
|
-
console.log(
|
|
1980
|
-
`[Memory Search] Found ${results.length} memories above similarity threshold ${minSimilarity}. Top similarity: ${results[0]?.similarity.toFixed(4) || "N/A"}`
|
|
1981
|
-
);
|
|
1982
|
-
}
|
|
1983
|
-
return results;
|
|
1984
|
-
};
|
|
1985
|
-
|
|
1986
2745
|
// src/client/sdk.gen.ts
|
|
1987
2746
|
var postApiV1Embeddings = (options) => {
|
|
1988
2747
|
return (options.client ?? client).post({
|
|
@@ -2091,70 +2850,11 @@ var generateEmbeddingForMemory = async (memory, options = {}) => {
|
|
|
2091
2850
|
].filter(Boolean).join(" ");
|
|
2092
2851
|
return generateEmbeddingForText(text, options);
|
|
2093
2852
|
};
|
|
2094
|
-
var generateEmbeddingsForMemories = async (memories, options = {}) => {
|
|
2095
|
-
const embeddings = /* @__PURE__ */ new Map();
|
|
2096
|
-
for (const memory of memories) {
|
|
2097
|
-
const uniqueKey = `${memory.namespace}:${memory.key}:${memory.value}`;
|
|
2098
|
-
try {
|
|
2099
|
-
const embedding = await generateEmbeddingForMemory(memory, options);
|
|
2100
|
-
embeddings.set(uniqueKey, embedding);
|
|
2101
|
-
} catch (error) {
|
|
2102
|
-
console.error(
|
|
2103
|
-
`Failed to generate embedding for memory ${uniqueKey}:`,
|
|
2104
|
-
error
|
|
2105
|
-
);
|
|
2106
|
-
}
|
|
2107
|
-
}
|
|
2108
|
-
return embeddings;
|
|
2109
|
-
};
|
|
2110
|
-
var updateMemoriesWithEmbeddings = async (embeddings, embeddingModel) => {
|
|
2111
|
-
const updates = Array.from(embeddings.entries()).map(
|
|
2112
|
-
async ([uniqueKey, embedding]) => {
|
|
2113
|
-
const existing = await memoryDb.memories.where("uniqueKey").equals(uniqueKey).first();
|
|
2114
|
-
if (existing?.id) {
|
|
2115
|
-
await memoryDb.memories.update(existing.id, {
|
|
2116
|
-
embedding,
|
|
2117
|
-
embeddingModel,
|
|
2118
|
-
updatedAt: Date.now(),
|
|
2119
|
-
createdAt: existing.createdAt
|
|
2120
|
-
});
|
|
2121
|
-
} else {
|
|
2122
|
-
console.warn(
|
|
2123
|
-
`[Embeddings] Memory with uniqueKey ${uniqueKey} not found. It may have been updated or deleted before embedding was generated.`
|
|
2124
|
-
);
|
|
2125
|
-
}
|
|
2126
|
-
}
|
|
2127
|
-
);
|
|
2128
|
-
await Promise.all(updates);
|
|
2129
|
-
};
|
|
2130
|
-
var generateAndStoreEmbeddings = async (memories, options = {}) => {
|
|
2131
|
-
let { model } = options;
|
|
2132
|
-
const { provider = "local" } = options;
|
|
2133
|
-
if (!model) {
|
|
2134
|
-
if (provider === "local") {
|
|
2135
|
-
model = DEFAULT_LOCAL_EMBEDDING_MODEL;
|
|
2136
|
-
} else {
|
|
2137
|
-
model = DEFAULT_API_EMBEDDING_MODEL;
|
|
2138
|
-
}
|
|
2139
|
-
}
|
|
2140
|
-
if (provider === "local" && model === DEFAULT_API_EMBEDDING_MODEL) {
|
|
2141
|
-
model = DEFAULT_LOCAL_EMBEDDING_MODEL;
|
|
2142
|
-
}
|
|
2143
|
-
if (memories.length === 0) {
|
|
2144
|
-
return;
|
|
2145
|
-
}
|
|
2146
|
-
console.log(`Generating embeddings for ${memories.length} memories...`);
|
|
2147
|
-
const embeddings = await generateEmbeddingsForMemories(memories, {
|
|
2148
|
-
...options,
|
|
2149
|
-
model
|
|
2150
|
-
});
|
|
2151
|
-
await updateMemoriesWithEmbeddings(embeddings, model);
|
|
2152
|
-
console.log(`Generated and stored ${embeddings.size} embeddings`);
|
|
2153
|
-
};
|
|
2154
2853
|
|
|
2155
|
-
// src/react/
|
|
2156
|
-
function
|
|
2854
|
+
// src/react/useMemoryStorage.ts
|
|
2855
|
+
function useMemoryStorage(options) {
|
|
2157
2856
|
const {
|
|
2857
|
+
database,
|
|
2158
2858
|
completionsModel = DEFAULT_COMPLETION_MODEL,
|
|
2159
2859
|
embeddingModel: userEmbeddingModel,
|
|
2160
2860
|
embeddingProvider = "local",
|
|
@@ -2164,10 +2864,39 @@ function useMemory(options = {}) {
|
|
|
2164
2864
|
baseUrl = BASE_URL
|
|
2165
2865
|
} = options;
|
|
2166
2866
|
const embeddingModel = userEmbeddingModel === void 0 ? embeddingProvider === "local" ? DEFAULT_LOCAL_EMBEDDING_MODEL : DEFAULT_API_EMBEDDING_MODEL : userEmbeddingModel;
|
|
2167
|
-
const
|
|
2168
|
-
const
|
|
2169
|
-
|
|
2170
|
-
|
|
2867
|
+
const [memories, setMemories] = (0, import_react3.useState)([]);
|
|
2868
|
+
const extractionInProgressRef = (0, import_react3.useRef)(false);
|
|
2869
|
+
const memoriesCollection = (0, import_react3.useMemo)(
|
|
2870
|
+
() => database.get("memories"),
|
|
2871
|
+
[database]
|
|
2872
|
+
);
|
|
2873
|
+
const storageCtx = (0, import_react3.useMemo)(
|
|
2874
|
+
() => ({
|
|
2875
|
+
database,
|
|
2876
|
+
memoriesCollection
|
|
2877
|
+
}),
|
|
2878
|
+
[database, memoriesCollection]
|
|
2879
|
+
);
|
|
2880
|
+
const effectiveEmbeddingModel = (0, import_react3.useMemo)(
|
|
2881
|
+
() => embeddingModel ?? (embeddingProvider === "api" ? DEFAULT_API_EMBEDDING_MODEL : DEFAULT_LOCAL_EMBEDDING_MODEL),
|
|
2882
|
+
[embeddingModel, embeddingProvider]
|
|
2883
|
+
);
|
|
2884
|
+
const embeddingOptions = (0, import_react3.useMemo)(
|
|
2885
|
+
() => ({
|
|
2886
|
+
model: effectiveEmbeddingModel,
|
|
2887
|
+
provider: embeddingProvider,
|
|
2888
|
+
getToken: getToken || void 0,
|
|
2889
|
+
baseUrl
|
|
2890
|
+
}),
|
|
2891
|
+
[effectiveEmbeddingModel, embeddingProvider, getToken, baseUrl]
|
|
2892
|
+
);
|
|
2893
|
+
const refreshMemories = (0, import_react3.useCallback)(async () => {
|
|
2894
|
+
const storedMemories = await getAllMemoriesOp(storageCtx);
|
|
2895
|
+
setMemories(storedMemories);
|
|
2896
|
+
}, [storageCtx]);
|
|
2897
|
+
const extractMemoriesFromMessage = (0, import_react3.useCallback)(
|
|
2898
|
+
async (opts) => {
|
|
2899
|
+
const { messages, model } = opts;
|
|
2171
2900
|
if (!getToken || extractionInProgressRef.current) {
|
|
2172
2901
|
return null;
|
|
2173
2902
|
}
|
|
@@ -2253,11 +2982,7 @@ function useMemory(options = {}) {
|
|
|
2253
2982
|
if (jsonObjectMatch && jsonObjectMatch[0]) {
|
|
2254
2983
|
jsonContent = jsonObjectMatch[0];
|
|
2255
2984
|
} else {
|
|
2256
|
-
console.warn(
|
|
2257
|
-
"Memory extraction returned non-JSON response. The model may not have found any memories to extract, or it returned natural language instead of JSON.",
|
|
2258
|
-
"\nFirst 200 chars of response:",
|
|
2259
|
-
content.substring(0, 200)
|
|
2260
|
-
);
|
|
2985
|
+
console.warn("Memory extraction returned non-JSON response");
|
|
2261
2986
|
return { items: [] };
|
|
2262
2987
|
}
|
|
2263
2988
|
}
|
|
@@ -2265,9 +2990,7 @@ function useMemory(options = {}) {
|
|
|
2265
2990
|
const trimmedJson = jsonContent.trim();
|
|
2266
2991
|
if (!trimmedJson.startsWith("{") || !trimmedJson.includes("items")) {
|
|
2267
2992
|
console.warn(
|
|
2268
|
-
"Memory extraction response doesn't appear to be valid JSON
|
|
2269
|
-
"\nResponse preview:",
|
|
2270
|
-
content.substring(0, 200)
|
|
2993
|
+
"Memory extraction response doesn't appear to be valid JSON"
|
|
2271
2994
|
);
|
|
2272
2995
|
return { items: [] };
|
|
2273
2996
|
}
|
|
@@ -2278,10 +3001,7 @@ function useMemory(options = {}) {
|
|
|
2278
3001
|
throw new Error("Invalid JSON structure: not an object");
|
|
2279
3002
|
}
|
|
2280
3003
|
if (!Array.isArray(result.items)) {
|
|
2281
|
-
console.warn(
|
|
2282
|
-
"Memory extraction result missing 'items' array. Result:",
|
|
2283
|
-
result
|
|
2284
|
-
);
|
|
3004
|
+
console.warn("Memory extraction result missing 'items' array");
|
|
2285
3005
|
return { items: [] };
|
|
2286
3006
|
}
|
|
2287
3007
|
} catch (parseError) {
|
|
@@ -2289,42 +3009,61 @@ function useMemory(options = {}) {
|
|
|
2289
3009
|
"Failed to parse memory extraction JSON:",
|
|
2290
3010
|
parseError instanceof Error ? parseError.message : parseError
|
|
2291
3011
|
);
|
|
2292
|
-
console.error("Attempted to parse:", jsonContent.substring(0, 200));
|
|
2293
|
-
console.error("Full raw content:", content.substring(0, 500));
|
|
2294
3012
|
return { items: [] };
|
|
2295
3013
|
}
|
|
2296
3014
|
if (result.items && Array.isArray(result.items)) {
|
|
2297
|
-
const originalCount = result.items.length;
|
|
2298
3015
|
result.items = preprocessMemories(result.items);
|
|
2299
|
-
const filteredCount = result.items.length;
|
|
2300
|
-
if (originalCount !== filteredCount) {
|
|
2301
|
-
console.log(
|
|
2302
|
-
`Preprocessed memories: ${originalCount} -> ${filteredCount} (dropped ${originalCount - filteredCount} entries)`
|
|
2303
|
-
);
|
|
2304
|
-
}
|
|
2305
3016
|
}
|
|
2306
|
-
console.log("Extracted memories:", JSON.stringify(result, null, 2));
|
|
2307
3017
|
if (result.items && result.items.length > 0) {
|
|
2308
3018
|
try {
|
|
2309
|
-
|
|
2310
|
-
|
|
3019
|
+
const createOptions = result.items.map(
|
|
3020
|
+
(item) => ({
|
|
3021
|
+
type: item.type,
|
|
3022
|
+
namespace: item.namespace,
|
|
3023
|
+
key: item.key,
|
|
3024
|
+
value: item.value,
|
|
3025
|
+
rawEvidence: item.rawEvidence,
|
|
3026
|
+
confidence: item.confidence,
|
|
3027
|
+
pii: item.pii
|
|
3028
|
+
})
|
|
3029
|
+
);
|
|
3030
|
+
const savedMemories = await saveMemoriesOp(storageCtx, createOptions);
|
|
3031
|
+
console.log(
|
|
3032
|
+
`Saved ${savedMemories.length} memories to WatermelonDB`
|
|
3033
|
+
);
|
|
2311
3034
|
if (generateEmbeddings && embeddingModel) {
|
|
2312
3035
|
try {
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
3036
|
+
for (const saved of savedMemories) {
|
|
3037
|
+
const memoryItem = {
|
|
3038
|
+
type: saved.type,
|
|
3039
|
+
namespace: saved.namespace,
|
|
3040
|
+
key: saved.key,
|
|
3041
|
+
value: saved.value,
|
|
3042
|
+
rawEvidence: saved.rawEvidence,
|
|
3043
|
+
confidence: saved.confidence,
|
|
3044
|
+
pii: saved.pii
|
|
3045
|
+
};
|
|
3046
|
+
const embedding = await generateEmbeddingForMemory(
|
|
3047
|
+
memoryItem,
|
|
3048
|
+
embeddingOptions
|
|
3049
|
+
);
|
|
3050
|
+
await updateMemoryEmbeddingOp(
|
|
3051
|
+
storageCtx,
|
|
3052
|
+
saved.uniqueId,
|
|
3053
|
+
embedding,
|
|
3054
|
+
effectiveEmbeddingModel
|
|
3055
|
+
);
|
|
3056
|
+
}
|
|
2319
3057
|
console.log(
|
|
2320
|
-
`Generated embeddings for ${
|
|
3058
|
+
`Generated embeddings for ${savedMemories.length} memories`
|
|
2321
3059
|
);
|
|
2322
3060
|
} catch (error) {
|
|
2323
3061
|
console.error("Failed to generate embeddings:", error);
|
|
2324
3062
|
}
|
|
2325
3063
|
}
|
|
3064
|
+
await refreshMemories();
|
|
2326
3065
|
} catch (error) {
|
|
2327
|
-
console.error("Failed to save memories to
|
|
3066
|
+
console.error("Failed to save memories to WatermelonDB:", error);
|
|
2328
3067
|
}
|
|
2329
3068
|
}
|
|
2330
3069
|
if (onFactsExtracted) {
|
|
@@ -2341,38 +3080,35 @@ function useMemory(options = {}) {
|
|
|
2341
3080
|
[
|
|
2342
3081
|
completionsModel,
|
|
2343
3082
|
embeddingModel,
|
|
2344
|
-
|
|
3083
|
+
embeddingOptions,
|
|
2345
3084
|
generateEmbeddings,
|
|
2346
3085
|
getToken,
|
|
2347
3086
|
onFactsExtracted,
|
|
2348
|
-
baseUrl
|
|
3087
|
+
baseUrl,
|
|
3088
|
+
storageCtx,
|
|
3089
|
+
refreshMemories
|
|
2349
3090
|
]
|
|
2350
3091
|
);
|
|
2351
|
-
const searchMemories = (0,
|
|
3092
|
+
const searchMemories = (0, import_react3.useCallback)(
|
|
2352
3093
|
async (query, limit = 10, minSimilarity = 0.6) => {
|
|
2353
3094
|
if (!embeddingModel) {
|
|
2354
3095
|
console.warn("Cannot search memories: embeddingModel not provided");
|
|
2355
3096
|
return [];
|
|
2356
3097
|
}
|
|
2357
3098
|
try {
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
provider: embeddingProvider,
|
|
2362
|
-
getToken,
|
|
2363
|
-
baseUrl
|
|
2364
|
-
});
|
|
2365
|
-
console.log(
|
|
2366
|
-
`[Memory Search] Generated query embedding (${queryEmbedding.length} dimensions)`
|
|
3099
|
+
const queryEmbedding = await generateEmbeddingForText(
|
|
3100
|
+
query,
|
|
3101
|
+
embeddingOptions
|
|
2367
3102
|
);
|
|
2368
|
-
const results = await
|
|
3103
|
+
const results = await searchSimilarMemoriesOp(
|
|
3104
|
+
storageCtx,
|
|
2369
3105
|
queryEmbedding,
|
|
2370
3106
|
limit,
|
|
2371
3107
|
minSimilarity
|
|
2372
3108
|
);
|
|
2373
3109
|
if (results.length === 0) {
|
|
2374
3110
|
console.warn(
|
|
2375
|
-
`[Memory Search] No memories found above similarity threshold ${minSimilarity}
|
|
3111
|
+
`[Memory Search] No memories found above similarity threshold ${minSimilarity}.`
|
|
2376
3112
|
);
|
|
2377
3113
|
} else {
|
|
2378
3114
|
console.log(
|
|
@@ -2380,175 +3116,282 @@ function useMemory(options = {}) {
|
|
|
2380
3116
|
);
|
|
2381
3117
|
}
|
|
2382
3118
|
return results;
|
|
2383
|
-
} catch
|
|
2384
|
-
console.error("Failed to search memories:", error);
|
|
3119
|
+
} catch {
|
|
2385
3120
|
return [];
|
|
2386
3121
|
}
|
|
2387
3122
|
},
|
|
2388
|
-
[embeddingModel,
|
|
3123
|
+
[embeddingModel, embeddingOptions, storageCtx]
|
|
2389
3124
|
);
|
|
2390
|
-
const fetchAllMemories = (0,
|
|
3125
|
+
const fetchAllMemories = (0, import_react3.useCallback)(async () => {
|
|
2391
3126
|
try {
|
|
2392
|
-
return await
|
|
3127
|
+
return await getAllMemoriesOp(storageCtx);
|
|
2393
3128
|
} catch (error) {
|
|
2394
3129
|
throw new Error(
|
|
2395
3130
|
"Failed to fetch all memories: " + (error instanceof Error ? error.message : String(error))
|
|
2396
3131
|
);
|
|
2397
3132
|
}
|
|
2398
|
-
}, []);
|
|
2399
|
-
const fetchMemoriesByNamespace = (0,
|
|
3133
|
+
}, [storageCtx]);
|
|
3134
|
+
const fetchMemoriesByNamespace = (0, import_react3.useCallback)(
|
|
2400
3135
|
async (namespace) => {
|
|
2401
3136
|
if (!namespace) {
|
|
2402
3137
|
throw new Error("Missing required field: namespace");
|
|
2403
3138
|
}
|
|
2404
3139
|
try {
|
|
2405
|
-
return await
|
|
3140
|
+
return await getMemoriesByNamespaceOp(storageCtx, namespace);
|
|
2406
3141
|
} catch (error) {
|
|
2407
3142
|
throw new Error(
|
|
2408
3143
|
`Failed to fetch memories for namespace "${namespace}": ` + (error instanceof Error ? error.message : String(error))
|
|
2409
3144
|
);
|
|
2410
3145
|
}
|
|
2411
3146
|
},
|
|
2412
|
-
[]
|
|
3147
|
+
[storageCtx]
|
|
2413
3148
|
);
|
|
2414
|
-
const fetchMemoriesByKey = (0,
|
|
3149
|
+
const fetchMemoriesByKey = (0, import_react3.useCallback)(
|
|
2415
3150
|
async (namespace, key) => {
|
|
2416
3151
|
if (!namespace || !key) {
|
|
2417
3152
|
throw new Error("Missing required fields: namespace, key");
|
|
2418
3153
|
}
|
|
2419
3154
|
try {
|
|
2420
|
-
return await
|
|
3155
|
+
return await getMemoriesByKeyOp(storageCtx, namespace, key);
|
|
2421
3156
|
} catch (error) {
|
|
2422
3157
|
throw new Error(
|
|
2423
3158
|
`Failed to fetch memories for "${namespace}:${key}": ` + (error instanceof Error ? error.message : String(error))
|
|
2424
3159
|
);
|
|
2425
3160
|
}
|
|
2426
3161
|
},
|
|
2427
|
-
[]
|
|
3162
|
+
[storageCtx]
|
|
2428
3163
|
);
|
|
2429
|
-
const
|
|
2430
|
-
async (id
|
|
2431
|
-
|
|
2432
|
-
|
|
3164
|
+
const getMemoryById = (0, import_react3.useCallback)(
|
|
3165
|
+
async (id) => {
|
|
3166
|
+
try {
|
|
3167
|
+
return await getMemoryByIdOp(storageCtx, id);
|
|
3168
|
+
} catch (error) {
|
|
3169
|
+
throw new Error(
|
|
3170
|
+
`Failed to get memory ${id}: ` + (error instanceof Error ? error.message : String(error))
|
|
3171
|
+
);
|
|
2433
3172
|
}
|
|
3173
|
+
},
|
|
3174
|
+
[storageCtx]
|
|
3175
|
+
);
|
|
3176
|
+
const saveMemory = (0, import_react3.useCallback)(
|
|
3177
|
+
async (memory) => {
|
|
2434
3178
|
try {
|
|
2435
|
-
const
|
|
2436
|
-
|
|
2437
|
-
model: embeddingModelToUse,
|
|
2438
|
-
provider: embeddingProvider,
|
|
2439
|
-
getToken: getToken || void 0,
|
|
2440
|
-
baseUrl
|
|
2441
|
-
};
|
|
2442
|
-
if (!updates.type || !updates.namespace || !updates.key || !updates.value || !updates.rawEvidence || updates.confidence === void 0 || updates.confidence === null || updates.pii === void 0 || updates.pii === null) {
|
|
2443
|
-
throw new Error(
|
|
2444
|
-
"Missing required fields: type, namespace, key, value, rawEvidence, confidence, pii"
|
|
2445
|
-
);
|
|
2446
|
-
}
|
|
2447
|
-
const existingMemory = await getMemoryById(id);
|
|
2448
|
-
if (!existingMemory) {
|
|
2449
|
-
throw new Error(`Memory with id ${id} not found`);
|
|
2450
|
-
}
|
|
2451
|
-
const embeddingFieldsChanged = updates.value !== void 0 && updates.value !== existingMemory.value || updates.rawEvidence !== void 0 && updates.rawEvidence !== existingMemory.rawEvidence || updates.type !== void 0 && updates.type !== existingMemory.type || updates.namespace !== void 0 && updates.namespace !== existingMemory.namespace || updates.key !== void 0 && updates.key !== existingMemory.key;
|
|
2452
|
-
if (!embeddingFieldsChanged) {
|
|
2453
|
-
return existingMemory;
|
|
2454
|
-
}
|
|
2455
|
-
const memory = {
|
|
2456
|
-
type: updates.type,
|
|
2457
|
-
namespace: updates.namespace,
|
|
2458
|
-
key: updates.key,
|
|
2459
|
-
value: updates.value,
|
|
2460
|
-
rawEvidence: updates.rawEvidence,
|
|
2461
|
-
confidence: updates.confidence,
|
|
2462
|
-
pii: updates.pii
|
|
2463
|
-
};
|
|
2464
|
-
let embedding = existingMemory.embedding ?? [];
|
|
2465
|
-
let embeddingModelToStore = existingMemory.embeddingModel ?? "";
|
|
2466
|
-
if (generateEmbeddings && embeddingModelToUse) {
|
|
3179
|
+
const saved = await saveMemoryOp(storageCtx, memory);
|
|
3180
|
+
if (generateEmbeddings && embeddingModel && !memory.embedding) {
|
|
2467
3181
|
try {
|
|
2468
|
-
|
|
2469
|
-
memory,
|
|
3182
|
+
const memoryItem = {
|
|
3183
|
+
type: memory.type,
|
|
3184
|
+
namespace: memory.namespace,
|
|
3185
|
+
key: memory.key,
|
|
3186
|
+
value: memory.value,
|
|
3187
|
+
rawEvidence: memory.rawEvidence,
|
|
3188
|
+
confidence: memory.confidence,
|
|
3189
|
+
pii: memory.pii
|
|
3190
|
+
};
|
|
3191
|
+
const embedding = await generateEmbeddingForMemory(
|
|
3192
|
+
memoryItem,
|
|
2470
3193
|
embeddingOptions
|
|
2471
3194
|
);
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
3195
|
+
await updateMemoryEmbeddingOp(
|
|
3196
|
+
storageCtx,
|
|
3197
|
+
saved.uniqueId,
|
|
3198
|
+
embedding,
|
|
3199
|
+
effectiveEmbeddingModel
|
|
2477
3200
|
);
|
|
3201
|
+
} catch (error) {
|
|
3202
|
+
console.error("Failed to generate embedding:", error);
|
|
2478
3203
|
}
|
|
2479
3204
|
}
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
3205
|
+
setMemories((prev) => {
|
|
3206
|
+
const existing = prev.find((m) => m.uniqueId === saved.uniqueId);
|
|
3207
|
+
if (existing) {
|
|
3208
|
+
return prev.map((m) => m.uniqueId === saved.uniqueId ? saved : m);
|
|
3209
|
+
}
|
|
3210
|
+
return [saved, ...prev];
|
|
3211
|
+
});
|
|
3212
|
+
return saved;
|
|
3213
|
+
} catch (error) {
|
|
3214
|
+
throw new Error(
|
|
3215
|
+
"Failed to save memory: " + (error instanceof Error ? error.message : String(error))
|
|
2486
3216
|
);
|
|
3217
|
+
}
|
|
3218
|
+
},
|
|
3219
|
+
[storageCtx, generateEmbeddings, embeddingModel, embeddingOptions]
|
|
3220
|
+
);
|
|
3221
|
+
const saveMemories = (0, import_react3.useCallback)(
|
|
3222
|
+
async (memoriesToSave) => {
|
|
3223
|
+
try {
|
|
3224
|
+
const saved = await saveMemoriesOp(storageCtx, memoriesToSave);
|
|
3225
|
+
if (generateEmbeddings && embeddingModel) {
|
|
3226
|
+
for (let i = 0; i < saved.length; i++) {
|
|
3227
|
+
const memory = memoriesToSave[i];
|
|
3228
|
+
if (!memory.embedding) {
|
|
3229
|
+
try {
|
|
3230
|
+
const memoryItem = {
|
|
3231
|
+
type: memory.type,
|
|
3232
|
+
namespace: memory.namespace,
|
|
3233
|
+
key: memory.key,
|
|
3234
|
+
value: memory.value,
|
|
3235
|
+
rawEvidence: memory.rawEvidence,
|
|
3236
|
+
confidence: memory.confidence,
|
|
3237
|
+
pii: memory.pii
|
|
3238
|
+
};
|
|
3239
|
+
const embedding = await generateEmbeddingForMemory(
|
|
3240
|
+
memoryItem,
|
|
3241
|
+
embeddingOptions
|
|
3242
|
+
);
|
|
3243
|
+
await updateMemoryEmbeddingOp(
|
|
3244
|
+
storageCtx,
|
|
3245
|
+
saved[i].uniqueId,
|
|
3246
|
+
embedding,
|
|
3247
|
+
effectiveEmbeddingModel
|
|
3248
|
+
);
|
|
3249
|
+
} catch (error) {
|
|
3250
|
+
console.error("Failed to generate embedding:", error);
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
await refreshMemories();
|
|
3256
|
+
return saved;
|
|
2487
3257
|
} catch (error) {
|
|
2488
3258
|
throw new Error(
|
|
2489
|
-
|
|
3259
|
+
"Failed to save memories: " + (error instanceof Error ? error.message : String(error))
|
|
3260
|
+
);
|
|
3261
|
+
}
|
|
3262
|
+
},
|
|
3263
|
+
[
|
|
3264
|
+
storageCtx,
|
|
3265
|
+
generateEmbeddings,
|
|
3266
|
+
embeddingModel,
|
|
3267
|
+
embeddingOptions,
|
|
3268
|
+
refreshMemories
|
|
3269
|
+
]
|
|
3270
|
+
);
|
|
3271
|
+
const updateMemory = (0, import_react3.useCallback)(
|
|
3272
|
+
async (id, updates) => {
|
|
3273
|
+
const result = await updateMemoryOp(storageCtx, id, updates);
|
|
3274
|
+
if (!result.ok) {
|
|
3275
|
+
if (result.reason === "not_found") {
|
|
3276
|
+
return null;
|
|
3277
|
+
}
|
|
3278
|
+
if (result.reason === "conflict") {
|
|
3279
|
+
throw new Error(
|
|
3280
|
+
`Cannot update memory: a memory with key "${result.conflictingKey}" already exists`
|
|
3281
|
+
);
|
|
3282
|
+
}
|
|
3283
|
+
throw new Error(
|
|
3284
|
+
`Failed to update memory ${id}: ${result.error.message}`
|
|
2490
3285
|
);
|
|
2491
3286
|
}
|
|
3287
|
+
const updated = result.memory;
|
|
3288
|
+
const contentChanged = updates.value !== void 0 || updates.rawEvidence !== void 0 || updates.type !== void 0 || updates.namespace !== void 0 || updates.key !== void 0;
|
|
3289
|
+
if (contentChanged && generateEmbeddings && embeddingModel && !updates.embedding) {
|
|
3290
|
+
try {
|
|
3291
|
+
const memoryItem = {
|
|
3292
|
+
type: updated.type,
|
|
3293
|
+
namespace: updated.namespace,
|
|
3294
|
+
key: updated.key,
|
|
3295
|
+
value: updated.value,
|
|
3296
|
+
rawEvidence: updated.rawEvidence,
|
|
3297
|
+
confidence: updated.confidence,
|
|
3298
|
+
pii: updated.pii
|
|
3299
|
+
};
|
|
3300
|
+
const embedding = await generateEmbeddingForMemory(
|
|
3301
|
+
memoryItem,
|
|
3302
|
+
embeddingOptions
|
|
3303
|
+
);
|
|
3304
|
+
await updateMemoryEmbeddingOp(
|
|
3305
|
+
storageCtx,
|
|
3306
|
+
id,
|
|
3307
|
+
embedding,
|
|
3308
|
+
effectiveEmbeddingModel
|
|
3309
|
+
);
|
|
3310
|
+
} catch (error) {
|
|
3311
|
+
console.error("Failed to regenerate embedding:", error);
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
setMemories(
|
|
3315
|
+
(prev) => prev.map((m) => m.uniqueId === id ? updated : m)
|
|
3316
|
+
);
|
|
3317
|
+
return updated;
|
|
2492
3318
|
},
|
|
2493
|
-
[
|
|
3319
|
+
[storageCtx, generateEmbeddings, embeddingModel, embeddingOptions]
|
|
2494
3320
|
);
|
|
2495
|
-
const removeMemory = (0,
|
|
3321
|
+
const removeMemory = (0, import_react3.useCallback)(
|
|
2496
3322
|
async (namespace, key, value) => {
|
|
2497
3323
|
if (!namespace || !key || !value) {
|
|
2498
3324
|
throw new Error("Missing required fields: namespace, key, value");
|
|
2499
3325
|
}
|
|
2500
3326
|
try {
|
|
2501
|
-
await
|
|
3327
|
+
await deleteMemoryOp(storageCtx, namespace, key, value);
|
|
3328
|
+
setMemories(
|
|
3329
|
+
(prev) => prev.filter(
|
|
3330
|
+
(m) => !(m.namespace === namespace && m.key === key && m.value === value)
|
|
3331
|
+
)
|
|
3332
|
+
);
|
|
2502
3333
|
} catch (error) {
|
|
2503
3334
|
throw new Error(
|
|
2504
3335
|
`Failed to delete memory "${namespace}:${key}:${value}": ` + (error instanceof Error ? error.message : String(error))
|
|
2505
3336
|
);
|
|
2506
3337
|
}
|
|
2507
3338
|
},
|
|
2508
|
-
[]
|
|
3339
|
+
[storageCtx]
|
|
2509
3340
|
);
|
|
2510
|
-
const removeMemoryById = (0,
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
}
|
|
2521
|
-
|
|
2522
|
-
|
|
3341
|
+
const removeMemoryById = (0, import_react3.useCallback)(
|
|
3342
|
+
async (id) => {
|
|
3343
|
+
try {
|
|
3344
|
+
await deleteMemoryByIdOp(storageCtx, id);
|
|
3345
|
+
setMemories((prev) => prev.filter((m) => m.uniqueId !== id));
|
|
3346
|
+
} catch (error) {
|
|
3347
|
+
throw new Error(
|
|
3348
|
+
`Failed to delete memory with id ${id}: ` + (error instanceof Error ? error.message : String(error))
|
|
3349
|
+
);
|
|
3350
|
+
}
|
|
3351
|
+
},
|
|
3352
|
+
[storageCtx]
|
|
3353
|
+
);
|
|
3354
|
+
const removeMemories = (0, import_react3.useCallback)(
|
|
2523
3355
|
async (namespace, key) => {
|
|
2524
3356
|
if (!namespace || !key) {
|
|
2525
3357
|
throw new Error("Missing required fields: namespace, key");
|
|
2526
3358
|
}
|
|
2527
3359
|
try {
|
|
2528
|
-
await
|
|
3360
|
+
await deleteMemoriesByKeyOp(storageCtx, namespace, key);
|
|
3361
|
+
setMemories(
|
|
3362
|
+
(prev) => prev.filter(
|
|
3363
|
+
(m) => !(m.namespace === namespace && m.key === key)
|
|
3364
|
+
)
|
|
3365
|
+
);
|
|
2529
3366
|
} catch (error) {
|
|
2530
3367
|
throw new Error(
|
|
2531
3368
|
`Failed to delete memories for "${namespace}:${key}": ` + (error instanceof Error ? error.message : String(error))
|
|
2532
3369
|
);
|
|
2533
3370
|
}
|
|
2534
3371
|
},
|
|
2535
|
-
[]
|
|
3372
|
+
[storageCtx]
|
|
2536
3373
|
);
|
|
2537
|
-
const clearMemories = (0,
|
|
3374
|
+
const clearMemories = (0, import_react3.useCallback)(async () => {
|
|
2538
3375
|
try {
|
|
2539
|
-
await
|
|
3376
|
+
await clearAllMemoriesOp(storageCtx);
|
|
3377
|
+
setMemories([]);
|
|
2540
3378
|
} catch (error) {
|
|
2541
3379
|
throw new Error(
|
|
2542
3380
|
"Failed to clear all memories: " + (error instanceof Error ? error.message : String(error))
|
|
2543
3381
|
);
|
|
2544
3382
|
}
|
|
2545
|
-
}, []);
|
|
3383
|
+
}, [storageCtx]);
|
|
2546
3384
|
return {
|
|
3385
|
+
memories,
|
|
3386
|
+
refreshMemories,
|
|
2547
3387
|
extractMemoriesFromMessage,
|
|
2548
3388
|
searchMemories,
|
|
2549
3389
|
fetchAllMemories,
|
|
2550
3390
|
fetchMemoriesByNamespace,
|
|
2551
3391
|
fetchMemoriesByKey,
|
|
3392
|
+
getMemoryById,
|
|
3393
|
+
saveMemory,
|
|
3394
|
+
saveMemories,
|
|
2552
3395
|
updateMemory,
|
|
2553
3396
|
removeMemory,
|
|
2554
3397
|
removeMemoryById,
|
|
@@ -2557,8 +3400,115 @@ function useMemory(options = {}) {
|
|
|
2557
3400
|
};
|
|
2558
3401
|
}
|
|
2559
3402
|
|
|
3403
|
+
// src/lib/memoryStorage/schema.ts
|
|
3404
|
+
var import_watermelondb5 = require("@nozbe/watermelondb");
|
|
3405
|
+
var memoryStorageSchema = (0, import_watermelondb5.appSchema)({
|
|
3406
|
+
version: 1,
|
|
3407
|
+
tables: [
|
|
3408
|
+
(0, import_watermelondb5.tableSchema)({
|
|
3409
|
+
name: "memories",
|
|
3410
|
+
columns: [
|
|
3411
|
+
// Memory type classification
|
|
3412
|
+
{ name: "type", type: "string", isIndexed: true },
|
|
3413
|
+
// 'identity' | 'preference' | 'project' | 'skill' | 'constraint'
|
|
3414
|
+
// Hierarchical key structure
|
|
3415
|
+
{ name: "namespace", type: "string", isIndexed: true },
|
|
3416
|
+
{ name: "key", type: "string", isIndexed: true },
|
|
3417
|
+
{ name: "value", type: "string" },
|
|
3418
|
+
// Evidence and confidence
|
|
3419
|
+
{ name: "raw_evidence", type: "string" },
|
|
3420
|
+
{ name: "confidence", type: "number" },
|
|
3421
|
+
{ name: "pii", type: "boolean", isIndexed: true },
|
|
3422
|
+
// Composite keys for efficient lookups
|
|
3423
|
+
{ name: "composite_key", type: "string", isIndexed: true },
|
|
3424
|
+
// namespace:key
|
|
3425
|
+
{ name: "unique_key", type: "string", isIndexed: true },
|
|
3426
|
+
// namespace:key:value
|
|
3427
|
+
// Timestamps
|
|
3428
|
+
{ name: "created_at", type: "number", isIndexed: true },
|
|
3429
|
+
{ name: "updated_at", type: "number" },
|
|
3430
|
+
// Vector embeddings for semantic search
|
|
3431
|
+
{ name: "embedding", type: "string", isOptional: true },
|
|
3432
|
+
// JSON stringified number[]
|
|
3433
|
+
{ name: "embedding_model", type: "string", isOptional: true },
|
|
3434
|
+
// Soft delete flag
|
|
3435
|
+
{ name: "is_deleted", type: "boolean", isIndexed: true }
|
|
3436
|
+
]
|
|
3437
|
+
})
|
|
3438
|
+
]
|
|
3439
|
+
});
|
|
3440
|
+
|
|
3441
|
+
// src/lib/memoryStorage/models.ts
|
|
3442
|
+
var import_watermelondb6 = require("@nozbe/watermelondb");
|
|
3443
|
+
var Memory = class extends import_watermelondb6.Model {
|
|
3444
|
+
/** Memory type classification */
|
|
3445
|
+
get type() {
|
|
3446
|
+
return this._getRaw("type");
|
|
3447
|
+
}
|
|
3448
|
+
/** Namespace for grouping related memories */
|
|
3449
|
+
get namespace() {
|
|
3450
|
+
return this._getRaw("namespace");
|
|
3451
|
+
}
|
|
3452
|
+
/** Key within the namespace */
|
|
3453
|
+
get key() {
|
|
3454
|
+
return this._getRaw("key");
|
|
3455
|
+
}
|
|
3456
|
+
/** The memory value/content */
|
|
3457
|
+
get value() {
|
|
3458
|
+
return this._getRaw("value");
|
|
3459
|
+
}
|
|
3460
|
+
/** Raw evidence from which this memory was extracted */
|
|
3461
|
+
get rawEvidence() {
|
|
3462
|
+
return this._getRaw("raw_evidence");
|
|
3463
|
+
}
|
|
3464
|
+
/** Confidence score (0-1) */
|
|
3465
|
+
get confidence() {
|
|
3466
|
+
return this._getRaw("confidence");
|
|
3467
|
+
}
|
|
3468
|
+
/** Whether this memory contains PII */
|
|
3469
|
+
get pii() {
|
|
3470
|
+
return this._getRaw("pii");
|
|
3471
|
+
}
|
|
3472
|
+
/** Composite key (namespace:key) for efficient lookups */
|
|
3473
|
+
get compositeKey() {
|
|
3474
|
+
return this._getRaw("composite_key");
|
|
3475
|
+
}
|
|
3476
|
+
/** Unique key (namespace:key:value) for deduplication */
|
|
3477
|
+
get uniqueKey() {
|
|
3478
|
+
return this._getRaw("unique_key");
|
|
3479
|
+
}
|
|
3480
|
+
/** Created timestamp */
|
|
3481
|
+
get createdAt() {
|
|
3482
|
+
return new Date(this._getRaw("created_at"));
|
|
3483
|
+
}
|
|
3484
|
+
/** Updated timestamp */
|
|
3485
|
+
get updatedAt() {
|
|
3486
|
+
return new Date(this._getRaw("updated_at"));
|
|
3487
|
+
}
|
|
3488
|
+
/** Embedding vector for semantic search */
|
|
3489
|
+
get embedding() {
|
|
3490
|
+
const raw = this._getRaw("embedding");
|
|
3491
|
+
if (!raw) return void 0;
|
|
3492
|
+
try {
|
|
3493
|
+
return JSON.parse(raw);
|
|
3494
|
+
} catch {
|
|
3495
|
+
return void 0;
|
|
3496
|
+
}
|
|
3497
|
+
}
|
|
3498
|
+
/** Model used to generate embedding */
|
|
3499
|
+
get embeddingModel() {
|
|
3500
|
+
const value = this._getRaw("embedding_model");
|
|
3501
|
+
return value ? value : void 0;
|
|
3502
|
+
}
|
|
3503
|
+
/** Soft delete flag */
|
|
3504
|
+
get isDeleted() {
|
|
3505
|
+
return this._getRaw("is_deleted");
|
|
3506
|
+
}
|
|
3507
|
+
};
|
|
3508
|
+
Memory.table = "memories";
|
|
3509
|
+
|
|
2560
3510
|
// src/react/usePdf.ts
|
|
2561
|
-
var
|
|
3511
|
+
var import_react4 = require("react");
|
|
2562
3512
|
|
|
2563
3513
|
// src/lib/pdf.ts
|
|
2564
3514
|
var pdfjs = __toESM(require("pdfjs-dist"));
|
|
@@ -2611,9 +3561,9 @@ async function convertPdfToImages(pdfDataUrl) {
|
|
|
2611
3561
|
// src/react/usePdf.ts
|
|
2612
3562
|
var PDF_MIME_TYPE = "application/pdf";
|
|
2613
3563
|
function usePdf() {
|
|
2614
|
-
const [isProcessing, setIsProcessing] = (0,
|
|
2615
|
-
const [error, setError] = (0,
|
|
2616
|
-
const extractPdfContext = (0,
|
|
3564
|
+
const [isProcessing, setIsProcessing] = (0, import_react4.useState)(false);
|
|
3565
|
+
const [error, setError] = (0, import_react4.useState)(null);
|
|
3566
|
+
const extractPdfContext = (0, import_react4.useCallback)(
|
|
2617
3567
|
async (files) => {
|
|
2618
3568
|
setIsProcessing(true);
|
|
2619
3569
|
setError(null);
|
|
@@ -2660,12 +3610,12 @@ ${text}`;
|
|
|
2660
3610
|
}
|
|
2661
3611
|
|
|
2662
3612
|
// src/react/useOCR.ts
|
|
2663
|
-
var
|
|
3613
|
+
var import_react5 = require("react");
|
|
2664
3614
|
var import_tesseract = __toESM(require("tesseract.js"));
|
|
2665
3615
|
function useOCR() {
|
|
2666
|
-
const [isProcessing, setIsProcessing] = (0,
|
|
2667
|
-
const [error, setError] = (0,
|
|
2668
|
-
const extractOCRContext = (0,
|
|
3616
|
+
const [isProcessing, setIsProcessing] = (0, import_react5.useState)(false);
|
|
3617
|
+
const [error, setError] = (0, import_react5.useState)(null);
|
|
3618
|
+
const extractOCRContext = (0, import_react5.useCallback)(
|
|
2669
3619
|
async (files) => {
|
|
2670
3620
|
setIsProcessing(true);
|
|
2671
3621
|
setError(null);
|
|
@@ -2751,22 +3701,22 @@ ${text}`;
|
|
|
2751
3701
|
}
|
|
2752
3702
|
|
|
2753
3703
|
// src/react/useModels.ts
|
|
2754
|
-
var
|
|
3704
|
+
var import_react6 = require("react");
|
|
2755
3705
|
function useModels(options = {}) {
|
|
2756
3706
|
const { getToken, baseUrl = BASE_URL, provider, autoFetch = true } = options;
|
|
2757
|
-
const [models, setModels] = (0,
|
|
2758
|
-
const [isLoading, setIsLoading] = (0,
|
|
2759
|
-
const [error, setError] = (0,
|
|
2760
|
-
const getTokenRef = (0,
|
|
2761
|
-
const baseUrlRef = (0,
|
|
2762
|
-
const providerRef = (0,
|
|
2763
|
-
const abortControllerRef = (0,
|
|
2764
|
-
(0,
|
|
3707
|
+
const [models, setModels] = (0, import_react6.useState)([]);
|
|
3708
|
+
const [isLoading, setIsLoading] = (0, import_react6.useState)(false);
|
|
3709
|
+
const [error, setError] = (0, import_react6.useState)(null);
|
|
3710
|
+
const getTokenRef = (0, import_react6.useRef)(getToken);
|
|
3711
|
+
const baseUrlRef = (0, import_react6.useRef)(baseUrl);
|
|
3712
|
+
const providerRef = (0, import_react6.useRef)(provider);
|
|
3713
|
+
const abortControllerRef = (0, import_react6.useRef)(null);
|
|
3714
|
+
(0, import_react6.useEffect)(() => {
|
|
2765
3715
|
getTokenRef.current = getToken;
|
|
2766
3716
|
baseUrlRef.current = baseUrl;
|
|
2767
3717
|
providerRef.current = provider;
|
|
2768
3718
|
});
|
|
2769
|
-
(0,
|
|
3719
|
+
(0, import_react6.useEffect)(() => {
|
|
2770
3720
|
return () => {
|
|
2771
3721
|
if (abortControllerRef.current) {
|
|
2772
3722
|
abortControllerRef.current.abort();
|
|
@@ -2774,7 +3724,7 @@ function useModels(options = {}) {
|
|
|
2774
3724
|
}
|
|
2775
3725
|
};
|
|
2776
3726
|
}, []);
|
|
2777
|
-
const fetchModels = (0,
|
|
3727
|
+
const fetchModels = (0, import_react6.useCallback)(async () => {
|
|
2778
3728
|
if (abortControllerRef.current) {
|
|
2779
3729
|
abortControllerRef.current.abort();
|
|
2780
3730
|
}
|
|
@@ -2832,12 +3782,12 @@ function useModels(options = {}) {
|
|
|
2832
3782
|
}
|
|
2833
3783
|
}
|
|
2834
3784
|
}, []);
|
|
2835
|
-
const refetch = (0,
|
|
3785
|
+
const refetch = (0, import_react6.useCallback)(async () => {
|
|
2836
3786
|
setModels([]);
|
|
2837
3787
|
await fetchModels();
|
|
2838
3788
|
}, [fetchModels]);
|
|
2839
|
-
const hasFetchedRef = (0,
|
|
2840
|
-
(0,
|
|
3789
|
+
const hasFetchedRef = (0, import_react6.useRef)(false);
|
|
3790
|
+
(0, import_react6.useEffect)(() => {
|
|
2841
3791
|
if (autoFetch && !hasFetchedRef.current) {
|
|
2842
3792
|
hasFetchedRef.current = true;
|
|
2843
3793
|
fetchModels();
|
|
@@ -2855,15 +3805,15 @@ function useModels(options = {}) {
|
|
|
2855
3805
|
}
|
|
2856
3806
|
|
|
2857
3807
|
// src/react/useSearch.ts
|
|
2858
|
-
var
|
|
3808
|
+
var import_react7 = require("react");
|
|
2859
3809
|
function useSearch(options = {}) {
|
|
2860
3810
|
const { getToken, baseUrl = BASE_URL, onError } = options;
|
|
2861
|
-
const [isLoading, setIsLoading] = (0,
|
|
2862
|
-
const [results, setResults] = (0,
|
|
2863
|
-
const [response, setResponse] = (0,
|
|
2864
|
-
const [error, setError] = (0,
|
|
2865
|
-
const abortControllerRef = (0,
|
|
2866
|
-
(0,
|
|
3811
|
+
const [isLoading, setIsLoading] = (0, import_react7.useState)(false);
|
|
3812
|
+
const [results, setResults] = (0, import_react7.useState)(null);
|
|
3813
|
+
const [response, setResponse] = (0, import_react7.useState)(null);
|
|
3814
|
+
const [error, setError] = (0, import_react7.useState)(null);
|
|
3815
|
+
const abortControllerRef = (0, import_react7.useRef)(null);
|
|
3816
|
+
(0, import_react7.useEffect)(() => {
|
|
2867
3817
|
return () => {
|
|
2868
3818
|
if (abortControllerRef.current) {
|
|
2869
3819
|
abortControllerRef.current.abort();
|
|
@@ -2871,7 +3821,7 @@ function useSearch(options = {}) {
|
|
|
2871
3821
|
}
|
|
2872
3822
|
};
|
|
2873
3823
|
}, []);
|
|
2874
|
-
const search = (0,
|
|
3824
|
+
const search = (0, import_react7.useCallback)(
|
|
2875
3825
|
async (query, searchOptions = {}) => {
|
|
2876
3826
|
if (abortControllerRef.current) {
|
|
2877
3827
|
abortControllerRef.current.abort();
|
|
@@ -2939,12 +3889,12 @@ function useSearch(options = {}) {
|
|
|
2939
3889
|
}
|
|
2940
3890
|
|
|
2941
3891
|
// src/react/useImageGeneration.ts
|
|
2942
|
-
var
|
|
3892
|
+
var import_react8 = require("react");
|
|
2943
3893
|
function useImageGeneration(options = {}) {
|
|
2944
3894
|
const { getToken, baseUrl = BASE_URL, onFinish, onError } = options;
|
|
2945
|
-
const [isLoading, setIsLoading] = (0,
|
|
2946
|
-
const abortControllerRef = (0,
|
|
2947
|
-
(0,
|
|
3895
|
+
const [isLoading, setIsLoading] = (0, import_react8.useState)(false);
|
|
3896
|
+
const abortControllerRef = (0, import_react8.useRef)(null);
|
|
3897
|
+
(0, import_react8.useEffect)(() => {
|
|
2948
3898
|
return () => {
|
|
2949
3899
|
if (abortControllerRef.current) {
|
|
2950
3900
|
abortControllerRef.current.abort();
|
|
@@ -2952,13 +3902,13 @@ function useImageGeneration(options = {}) {
|
|
|
2952
3902
|
}
|
|
2953
3903
|
};
|
|
2954
3904
|
}, []);
|
|
2955
|
-
const stop = (0,
|
|
3905
|
+
const stop = (0, import_react8.useCallback)(() => {
|
|
2956
3906
|
if (abortControllerRef.current) {
|
|
2957
3907
|
abortControllerRef.current.abort();
|
|
2958
3908
|
abortControllerRef.current = null;
|
|
2959
3909
|
}
|
|
2960
3910
|
}, []);
|
|
2961
|
-
const generateImage = (0,
|
|
3911
|
+
const generateImage = (0, import_react8.useCallback)(
|
|
2962
3912
|
async (args) => {
|
|
2963
3913
|
if (abortControllerRef.current) {
|
|
2964
3914
|
abortControllerRef.current.abort();
|
|
@@ -3076,7 +4026,11 @@ var extractConversationContext = (messages, maxMessages = 3) => {
|
|
|
3076
4026
|
};
|
|
3077
4027
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3078
4028
|
0 && (module.exports = {
|
|
4029
|
+
ChatConversation,
|
|
4030
|
+
ChatMessage,
|
|
3079
4031
|
DEFAULT_TOOL_SELECTOR_MODEL,
|
|
4032
|
+
StoredMemoryModel,
|
|
4033
|
+
chatStorageSchema,
|
|
3080
4034
|
createMemoryContextSystemMessage,
|
|
3081
4035
|
decryptData,
|
|
3082
4036
|
decryptDataBytes,
|
|
@@ -3084,13 +4038,18 @@ var extractConversationContext = (messages, maxMessages = 3) => {
|
|
|
3084
4038
|
executeTool,
|
|
3085
4039
|
extractConversationContext,
|
|
3086
4040
|
formatMemoriesForChat,
|
|
4041
|
+
generateCompositeKey,
|
|
4042
|
+
generateConversationId,
|
|
4043
|
+
generateUniqueKey,
|
|
3087
4044
|
hasEncryptionKey,
|
|
4045
|
+
memoryStorageSchema,
|
|
3088
4046
|
requestEncryptionKey,
|
|
3089
4047
|
selectTool,
|
|
3090
4048
|
useChat,
|
|
4049
|
+
useChatStorage,
|
|
3091
4050
|
useEncryption,
|
|
3092
4051
|
useImageGeneration,
|
|
3093
|
-
|
|
4052
|
+
useMemoryStorage,
|
|
3094
4053
|
useModels,
|
|
3095
4054
|
useOCR,
|
|
3096
4055
|
usePdf,
|