@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.mjs
CHANGED
|
@@ -1226,7 +1226,8 @@ function useChat(options) {
|
|
|
1226
1226
|
model,
|
|
1227
1227
|
onData,
|
|
1228
1228
|
runTools = true,
|
|
1229
|
-
headers
|
|
1229
|
+
headers,
|
|
1230
|
+
memoryContext
|
|
1230
1231
|
}) => {
|
|
1231
1232
|
const messagesValidation = validateMessages(messages);
|
|
1232
1233
|
if (!messagesValidation.valid) {
|
|
@@ -1239,7 +1240,20 @@ function useChat(options) {
|
|
|
1239
1240
|
abortControllerRef.current = abortController;
|
|
1240
1241
|
setIsLoading(true);
|
|
1241
1242
|
let toolExecutionResult;
|
|
1242
|
-
let
|
|
1243
|
+
let messagesWithContext = messages;
|
|
1244
|
+
if (memoryContext) {
|
|
1245
|
+
const memorySystemMessage = {
|
|
1246
|
+
role: "system",
|
|
1247
|
+
content: [
|
|
1248
|
+
{
|
|
1249
|
+
type: "text",
|
|
1250
|
+
text: memoryContext
|
|
1251
|
+
}
|
|
1252
|
+
]
|
|
1253
|
+
};
|
|
1254
|
+
messagesWithContext = [memorySystemMessage, ...messages];
|
|
1255
|
+
}
|
|
1256
|
+
let messagesWithToolContext = messagesWithContext;
|
|
1243
1257
|
const shouldRunTools = runTools && tools && tools.length > 0;
|
|
1244
1258
|
if (shouldRunTools) {
|
|
1245
1259
|
const lastUserMessage = [...messages].reverse().find((m) => m.role === "user");
|
|
@@ -1622,10 +1636,920 @@ function useEncryption(signMessage) {
|
|
|
1622
1636
|
};
|
|
1623
1637
|
}
|
|
1624
1638
|
|
|
1625
|
-
// src/react/
|
|
1626
|
-
import { useCallback as useCallback2,
|
|
1639
|
+
// src/react/useChatStorage.ts
|
|
1640
|
+
import { useCallback as useCallback2, useState as useState2, useMemo } from "react";
|
|
1641
|
+
|
|
1642
|
+
// src/lib/chatStorage/types.ts
|
|
1643
|
+
function convertUsageToStored(usage) {
|
|
1644
|
+
if (!usage) return void 0;
|
|
1645
|
+
return {
|
|
1646
|
+
promptTokens: usage.prompt_tokens,
|
|
1647
|
+
completionTokens: usage.completion_tokens,
|
|
1648
|
+
totalTokens: usage.total_tokens,
|
|
1649
|
+
costMicroUsd: usage.cost_micro_usd
|
|
1650
|
+
};
|
|
1651
|
+
}
|
|
1652
|
+
function generateConversationId() {
|
|
1653
|
+
return `conv_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
// src/lib/chatStorage/operations.ts
|
|
1657
|
+
import { Q } from "@nozbe/watermelondb";
|
|
1658
|
+
function messageToStored(message) {
|
|
1659
|
+
return {
|
|
1660
|
+
uniqueId: message.id,
|
|
1661
|
+
messageId: message.messageId,
|
|
1662
|
+
conversationId: message.conversationId,
|
|
1663
|
+
role: message.role,
|
|
1664
|
+
content: message.content,
|
|
1665
|
+
model: message.model,
|
|
1666
|
+
files: message.files,
|
|
1667
|
+
createdAt: message.createdAt,
|
|
1668
|
+
updatedAt: message.updatedAt,
|
|
1669
|
+
vector: message.vector,
|
|
1670
|
+
embeddingModel: message.embeddingModel,
|
|
1671
|
+
usage: message.usage,
|
|
1672
|
+
sources: message.sources,
|
|
1673
|
+
responseDuration: message.responseDuration
|
|
1674
|
+
};
|
|
1675
|
+
}
|
|
1676
|
+
function conversationToStored(conversation) {
|
|
1677
|
+
return {
|
|
1678
|
+
uniqueId: conversation.id,
|
|
1679
|
+
conversationId: conversation.conversationId,
|
|
1680
|
+
title: conversation.title,
|
|
1681
|
+
createdAt: conversation.createdAt,
|
|
1682
|
+
updatedAt: conversation.updatedAt,
|
|
1683
|
+
isDeleted: conversation.isDeleted
|
|
1684
|
+
};
|
|
1685
|
+
}
|
|
1686
|
+
async function createConversationOp(ctx, opts, defaultTitle = "New Conversation") {
|
|
1687
|
+
const convId = opts?.conversationId || generateConversationId();
|
|
1688
|
+
const title = opts?.title || defaultTitle;
|
|
1689
|
+
const created = await ctx.database.write(async () => {
|
|
1690
|
+
return await ctx.conversationsCollection.create((conv) => {
|
|
1691
|
+
conv._setRaw("conversation_id", convId);
|
|
1692
|
+
conv._setRaw("title", title);
|
|
1693
|
+
conv._setRaw("is_deleted", false);
|
|
1694
|
+
});
|
|
1695
|
+
});
|
|
1696
|
+
return conversationToStored(created);
|
|
1697
|
+
}
|
|
1698
|
+
async function getConversationOp(ctx, id) {
|
|
1699
|
+
const results = await ctx.conversationsCollection.query(Q.where("conversation_id", id), Q.where("is_deleted", false)).fetch();
|
|
1700
|
+
return results.length > 0 ? conversationToStored(results[0]) : null;
|
|
1701
|
+
}
|
|
1702
|
+
async function getConversationsOp(ctx) {
|
|
1703
|
+
const results = await ctx.conversationsCollection.query(Q.where("is_deleted", false), Q.sortBy("created_at", Q.desc)).fetch();
|
|
1704
|
+
return results.map(conversationToStored);
|
|
1705
|
+
}
|
|
1706
|
+
async function updateConversationTitleOp(ctx, id, title) {
|
|
1707
|
+
const results = await ctx.conversationsCollection.query(Q.where("conversation_id", id), Q.where("is_deleted", false)).fetch();
|
|
1708
|
+
if (results.length > 0) {
|
|
1709
|
+
await ctx.database.write(async () => {
|
|
1710
|
+
await results[0].update((conv) => {
|
|
1711
|
+
conv._setRaw("title", title);
|
|
1712
|
+
});
|
|
1713
|
+
});
|
|
1714
|
+
return true;
|
|
1715
|
+
}
|
|
1716
|
+
return false;
|
|
1717
|
+
}
|
|
1718
|
+
async function deleteConversationOp(ctx, id) {
|
|
1719
|
+
const results = await ctx.conversationsCollection.query(Q.where("conversation_id", id), Q.where("is_deleted", false)).fetch();
|
|
1720
|
+
if (results.length > 0) {
|
|
1721
|
+
await ctx.database.write(async () => {
|
|
1722
|
+
await results[0].update((conv) => {
|
|
1723
|
+
conv._setRaw("is_deleted", true);
|
|
1724
|
+
});
|
|
1725
|
+
});
|
|
1726
|
+
return true;
|
|
1727
|
+
}
|
|
1728
|
+
return false;
|
|
1729
|
+
}
|
|
1730
|
+
async function getMessagesOp(ctx, convId) {
|
|
1731
|
+
const results = await ctx.messagesCollection.query(Q.where("conversation_id", convId), Q.sortBy("message_id", Q.asc)).fetch();
|
|
1732
|
+
return results.map(messageToStored);
|
|
1733
|
+
}
|
|
1734
|
+
async function getMessageCountOp(ctx, convId) {
|
|
1735
|
+
return await ctx.messagesCollection.query(Q.where("conversation_id", convId)).fetchCount();
|
|
1736
|
+
}
|
|
1737
|
+
async function clearMessagesOp(ctx, convId) {
|
|
1738
|
+
const messages = await ctx.messagesCollection.query(Q.where("conversation_id", convId)).fetch();
|
|
1739
|
+
await ctx.database.write(async () => {
|
|
1740
|
+
for (const message of messages) {
|
|
1741
|
+
await message.destroyPermanently();
|
|
1742
|
+
}
|
|
1743
|
+
});
|
|
1744
|
+
}
|
|
1745
|
+
async function createMessageOp(ctx, opts) {
|
|
1746
|
+
const existingCount = await getMessageCountOp(ctx, opts.conversationId);
|
|
1747
|
+
const messageId = existingCount + 1;
|
|
1748
|
+
const created = await ctx.database.write(async () => {
|
|
1749
|
+
return await ctx.messagesCollection.create((msg) => {
|
|
1750
|
+
msg._setRaw("message_id", messageId);
|
|
1751
|
+
msg._setRaw("conversation_id", opts.conversationId);
|
|
1752
|
+
msg._setRaw("role", opts.role);
|
|
1753
|
+
msg._setRaw("content", opts.content);
|
|
1754
|
+
if (opts.model) msg._setRaw("model", opts.model);
|
|
1755
|
+
if (opts.files) msg._setRaw("files", JSON.stringify(opts.files));
|
|
1756
|
+
if (opts.usage) msg._setRaw("usage", JSON.stringify(opts.usage));
|
|
1757
|
+
if (opts.sources) msg._setRaw("sources", JSON.stringify(opts.sources));
|
|
1758
|
+
if (opts.responseDuration !== void 0)
|
|
1759
|
+
msg._setRaw("response_duration", opts.responseDuration);
|
|
1760
|
+
if (opts.vector) msg._setRaw("vector", JSON.stringify(opts.vector));
|
|
1761
|
+
if (opts.embeddingModel) msg._setRaw("embedding_model", opts.embeddingModel);
|
|
1762
|
+
});
|
|
1763
|
+
});
|
|
1764
|
+
return messageToStored(created);
|
|
1765
|
+
}
|
|
1766
|
+
async function updateMessageEmbeddingOp(ctx, uniqueId, vector, embeddingModel) {
|
|
1767
|
+
let message;
|
|
1768
|
+
try {
|
|
1769
|
+
message = await ctx.messagesCollection.find(uniqueId);
|
|
1770
|
+
} catch {
|
|
1771
|
+
return null;
|
|
1772
|
+
}
|
|
1773
|
+
await ctx.database.write(async () => {
|
|
1774
|
+
await message.update((msg) => {
|
|
1775
|
+
msg._setRaw("vector", JSON.stringify(vector));
|
|
1776
|
+
msg._setRaw("embedding_model", embeddingModel);
|
|
1777
|
+
});
|
|
1778
|
+
});
|
|
1779
|
+
return messageToStored(message);
|
|
1780
|
+
}
|
|
1781
|
+
function cosineSimilarity(a, b) {
|
|
1782
|
+
if (a.length !== b.length) return 0;
|
|
1783
|
+
let dotProduct = 0;
|
|
1784
|
+
let normA = 0;
|
|
1785
|
+
let normB = 0;
|
|
1786
|
+
for (let i = 0; i < a.length; i++) {
|
|
1787
|
+
dotProduct += a[i] * b[i];
|
|
1788
|
+
normA += a[i] * a[i];
|
|
1789
|
+
normB += b[i] * b[i];
|
|
1790
|
+
}
|
|
1791
|
+
const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
|
|
1792
|
+
return magnitude === 0 ? 0 : dotProduct / magnitude;
|
|
1793
|
+
}
|
|
1794
|
+
async function searchMessagesOp(ctx, queryVector, options) {
|
|
1795
|
+
const { limit = 10, minSimilarity = 0.5, conversationId } = options || {};
|
|
1796
|
+
const activeConversations = await ctx.conversationsCollection.query(Q.where("is_deleted", false)).fetch();
|
|
1797
|
+
const activeConversationIds = new Set(
|
|
1798
|
+
activeConversations.map((c) => c.conversationId)
|
|
1799
|
+
);
|
|
1800
|
+
const queryConditions = conversationId ? [Q.where("conversation_id", conversationId)] : [];
|
|
1801
|
+
const messages = await ctx.messagesCollection.query(...queryConditions).fetch();
|
|
1802
|
+
const resultsWithSimilarity = [];
|
|
1803
|
+
for (const message of messages) {
|
|
1804
|
+
if (!activeConversationIds.has(message.conversationId)) continue;
|
|
1805
|
+
const messageVector = message.vector;
|
|
1806
|
+
if (!messageVector || messageVector.length === 0) continue;
|
|
1807
|
+
const similarity = cosineSimilarity(queryVector, messageVector);
|
|
1808
|
+
if (similarity >= minSimilarity) {
|
|
1809
|
+
resultsWithSimilarity.push({
|
|
1810
|
+
...messageToStored(message),
|
|
1811
|
+
similarity
|
|
1812
|
+
});
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
return resultsWithSimilarity.sort((a, b) => b.similarity - a.similarity).slice(0, limit);
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
// src/react/useChatStorage.ts
|
|
1819
|
+
function storedToLlmapiMessage(stored) {
|
|
1820
|
+
const content = [
|
|
1821
|
+
{ type: "text", text: stored.content }
|
|
1822
|
+
];
|
|
1823
|
+
if (stored.files?.length) {
|
|
1824
|
+
for (const file of stored.files) {
|
|
1825
|
+
if (file.url) {
|
|
1826
|
+
content.push({
|
|
1827
|
+
type: "image_url",
|
|
1828
|
+
image_url: { url: file.url }
|
|
1829
|
+
});
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
return {
|
|
1834
|
+
role: stored.role,
|
|
1835
|
+
content
|
|
1836
|
+
};
|
|
1837
|
+
}
|
|
1838
|
+
function useChatStorage(options) {
|
|
1839
|
+
const {
|
|
1840
|
+
database,
|
|
1841
|
+
conversationId: initialConversationId,
|
|
1842
|
+
autoCreateConversation = true,
|
|
1843
|
+
defaultConversationTitle = "New Conversation",
|
|
1844
|
+
getToken,
|
|
1845
|
+
baseUrl,
|
|
1846
|
+
onData,
|
|
1847
|
+
onFinish,
|
|
1848
|
+
onError,
|
|
1849
|
+
chatProvider,
|
|
1850
|
+
localModel,
|
|
1851
|
+
tools,
|
|
1852
|
+
toolSelectorModel,
|
|
1853
|
+
onToolExecution
|
|
1854
|
+
} = options;
|
|
1855
|
+
const [currentConversationId, setCurrentConversationId] = useState2(initialConversationId || null);
|
|
1856
|
+
const messagesCollection = useMemo(
|
|
1857
|
+
() => database.get("history"),
|
|
1858
|
+
[database]
|
|
1859
|
+
);
|
|
1860
|
+
const conversationsCollection = useMemo(
|
|
1861
|
+
() => database.get("conversations"),
|
|
1862
|
+
[database]
|
|
1863
|
+
);
|
|
1864
|
+
const storageCtx = useMemo(
|
|
1865
|
+
() => ({
|
|
1866
|
+
database,
|
|
1867
|
+
messagesCollection,
|
|
1868
|
+
conversationsCollection
|
|
1869
|
+
}),
|
|
1870
|
+
[database, messagesCollection, conversationsCollection]
|
|
1871
|
+
);
|
|
1872
|
+
const {
|
|
1873
|
+
isLoading,
|
|
1874
|
+
isSelectingTool,
|
|
1875
|
+
sendMessage: baseSendMessage,
|
|
1876
|
+
stop
|
|
1877
|
+
} = useChat({
|
|
1878
|
+
getToken,
|
|
1879
|
+
baseUrl,
|
|
1880
|
+
onData,
|
|
1881
|
+
onFinish,
|
|
1882
|
+
onError,
|
|
1883
|
+
chatProvider,
|
|
1884
|
+
localModel,
|
|
1885
|
+
tools,
|
|
1886
|
+
toolSelectorModel,
|
|
1887
|
+
onToolExecution
|
|
1888
|
+
});
|
|
1889
|
+
const createConversation = useCallback2(
|
|
1890
|
+
async (opts) => {
|
|
1891
|
+
const created = await createConversationOp(
|
|
1892
|
+
storageCtx,
|
|
1893
|
+
opts,
|
|
1894
|
+
defaultConversationTitle
|
|
1895
|
+
);
|
|
1896
|
+
setCurrentConversationId(created.conversationId);
|
|
1897
|
+
return created;
|
|
1898
|
+
},
|
|
1899
|
+
[storageCtx, defaultConversationTitle]
|
|
1900
|
+
);
|
|
1901
|
+
const getConversation = useCallback2(
|
|
1902
|
+
async (id) => {
|
|
1903
|
+
return getConversationOp(storageCtx, id);
|
|
1904
|
+
},
|
|
1905
|
+
[storageCtx]
|
|
1906
|
+
);
|
|
1907
|
+
const getConversations = useCallback2(async () => {
|
|
1908
|
+
return getConversationsOp(storageCtx);
|
|
1909
|
+
}, [storageCtx]);
|
|
1910
|
+
const updateConversationTitle = useCallback2(
|
|
1911
|
+
async (id, title) => {
|
|
1912
|
+
return updateConversationTitleOp(storageCtx, id, title);
|
|
1913
|
+
},
|
|
1914
|
+
[storageCtx]
|
|
1915
|
+
);
|
|
1916
|
+
const deleteConversation = useCallback2(
|
|
1917
|
+
async (id) => {
|
|
1918
|
+
const deleted = await deleteConversationOp(storageCtx, id);
|
|
1919
|
+
if (deleted && currentConversationId === id) {
|
|
1920
|
+
setCurrentConversationId(null);
|
|
1921
|
+
}
|
|
1922
|
+
return deleted;
|
|
1923
|
+
},
|
|
1924
|
+
[storageCtx, currentConversationId]
|
|
1925
|
+
);
|
|
1926
|
+
const getMessages = useCallback2(
|
|
1927
|
+
async (convId) => {
|
|
1928
|
+
return getMessagesOp(storageCtx, convId);
|
|
1929
|
+
},
|
|
1930
|
+
[storageCtx]
|
|
1931
|
+
);
|
|
1932
|
+
const getMessageCount = useCallback2(
|
|
1933
|
+
async (convId) => {
|
|
1934
|
+
return getMessageCountOp(storageCtx, convId);
|
|
1935
|
+
},
|
|
1936
|
+
[storageCtx]
|
|
1937
|
+
);
|
|
1938
|
+
const clearMessages = useCallback2(
|
|
1939
|
+
async (convId) => {
|
|
1940
|
+
return clearMessagesOp(storageCtx, convId);
|
|
1941
|
+
},
|
|
1942
|
+
[storageCtx]
|
|
1943
|
+
);
|
|
1944
|
+
const ensureConversation = useCallback2(async () => {
|
|
1945
|
+
if (currentConversationId) {
|
|
1946
|
+
const existing = await getConversation(currentConversationId);
|
|
1947
|
+
if (existing) {
|
|
1948
|
+
return currentConversationId;
|
|
1949
|
+
}
|
|
1950
|
+
if (autoCreateConversation) {
|
|
1951
|
+
const newConv = await createConversation({
|
|
1952
|
+
conversationId: currentConversationId
|
|
1953
|
+
});
|
|
1954
|
+
return newConv.conversationId;
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
if (autoCreateConversation) {
|
|
1958
|
+
const newConv = await createConversation();
|
|
1959
|
+
return newConv.conversationId;
|
|
1960
|
+
}
|
|
1961
|
+
throw new Error(
|
|
1962
|
+
"No conversation ID provided and autoCreateConversation is disabled"
|
|
1963
|
+
);
|
|
1964
|
+
}, [
|
|
1965
|
+
currentConversationId,
|
|
1966
|
+
getConversation,
|
|
1967
|
+
autoCreateConversation,
|
|
1968
|
+
createConversation
|
|
1969
|
+
]);
|
|
1970
|
+
const sendMessage = useCallback2(
|
|
1971
|
+
async (args) => {
|
|
1972
|
+
const {
|
|
1973
|
+
content,
|
|
1974
|
+
model,
|
|
1975
|
+
messages: providedMessages,
|
|
1976
|
+
includeHistory = true,
|
|
1977
|
+
maxHistoryMessages = 50,
|
|
1978
|
+
files,
|
|
1979
|
+
onData: perRequestOnData,
|
|
1980
|
+
runTools,
|
|
1981
|
+
headers,
|
|
1982
|
+
memoryContext
|
|
1983
|
+
} = args;
|
|
1984
|
+
let convId;
|
|
1985
|
+
try {
|
|
1986
|
+
convId = await ensureConversation();
|
|
1987
|
+
} catch (err) {
|
|
1988
|
+
return {
|
|
1989
|
+
data: null,
|
|
1990
|
+
error: err instanceof Error ? err.message : "Failed to ensure conversation"
|
|
1991
|
+
};
|
|
1992
|
+
}
|
|
1993
|
+
let messagesToSend = [];
|
|
1994
|
+
if (includeHistory && !providedMessages) {
|
|
1995
|
+
const storedMessages = await getMessages(convId);
|
|
1996
|
+
const limitedMessages = storedMessages.slice(-maxHistoryMessages);
|
|
1997
|
+
messagesToSend = limitedMessages.map(storedToLlmapiMessage);
|
|
1998
|
+
} else if (providedMessages) {
|
|
1999
|
+
messagesToSend = providedMessages;
|
|
2000
|
+
}
|
|
2001
|
+
const userMessageContent = [
|
|
2002
|
+
{ type: "text", text: content }
|
|
2003
|
+
];
|
|
2004
|
+
if (files && files.length > 0) {
|
|
2005
|
+
for (const file of files) {
|
|
2006
|
+
if (file.url && file.type.startsWith("image/")) {
|
|
2007
|
+
userMessageContent.push({
|
|
2008
|
+
type: "image_url",
|
|
2009
|
+
image_url: { url: file.url }
|
|
2010
|
+
});
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
const userMessage = {
|
|
2015
|
+
role: "user",
|
|
2016
|
+
content: userMessageContent
|
|
2017
|
+
};
|
|
2018
|
+
messagesToSend.push(userMessage);
|
|
2019
|
+
const sanitizedFiles = files?.map((file) => ({
|
|
2020
|
+
id: file.id,
|
|
2021
|
+
name: file.name,
|
|
2022
|
+
type: file.type,
|
|
2023
|
+
size: file.size,
|
|
2024
|
+
// Only keep URL if it's not a data URI (e.g., external URLs)
|
|
2025
|
+
url: file.url && !file.url.startsWith("data:") ? file.url : void 0
|
|
2026
|
+
}));
|
|
2027
|
+
let storedUserMessage;
|
|
2028
|
+
try {
|
|
2029
|
+
storedUserMessage = await createMessageOp(storageCtx, {
|
|
2030
|
+
conversationId: convId,
|
|
2031
|
+
role: "user",
|
|
2032
|
+
content,
|
|
2033
|
+
files: sanitizedFiles
|
|
2034
|
+
});
|
|
2035
|
+
} catch (err) {
|
|
2036
|
+
return {
|
|
2037
|
+
data: null,
|
|
2038
|
+
error: err instanceof Error ? err.message : "Failed to store user message"
|
|
2039
|
+
};
|
|
2040
|
+
}
|
|
2041
|
+
const startTime = Date.now();
|
|
2042
|
+
const result = await baseSendMessage({
|
|
2043
|
+
messages: messagesToSend,
|
|
2044
|
+
model,
|
|
2045
|
+
onData: perRequestOnData,
|
|
2046
|
+
runTools,
|
|
2047
|
+
headers,
|
|
2048
|
+
memoryContext
|
|
2049
|
+
});
|
|
2050
|
+
const responseDuration = (Date.now() - startTime) / 1e3;
|
|
2051
|
+
if (result.error || !result.data) {
|
|
2052
|
+
return {
|
|
2053
|
+
data: null,
|
|
2054
|
+
error: result.error || "No response data received",
|
|
2055
|
+
toolExecution: result.toolExecution,
|
|
2056
|
+
userMessage: storedUserMessage
|
|
2057
|
+
};
|
|
2058
|
+
}
|
|
2059
|
+
const responseData = result.data;
|
|
2060
|
+
const assistantContent = responseData.choices?.[0]?.message?.content?.map((part) => part.text || "").join("") || "";
|
|
2061
|
+
let storedAssistantMessage;
|
|
2062
|
+
try {
|
|
2063
|
+
storedAssistantMessage = await createMessageOp(storageCtx, {
|
|
2064
|
+
conversationId: convId,
|
|
2065
|
+
role: "assistant",
|
|
2066
|
+
content: assistantContent,
|
|
2067
|
+
model: responseData.model,
|
|
2068
|
+
usage: convertUsageToStored(responseData.usage),
|
|
2069
|
+
responseDuration
|
|
2070
|
+
});
|
|
2071
|
+
} catch (err) {
|
|
2072
|
+
return {
|
|
2073
|
+
data: null,
|
|
2074
|
+
error: err instanceof Error ? err.message : "Failed to store assistant message",
|
|
2075
|
+
toolExecution: result.toolExecution,
|
|
2076
|
+
userMessage: storedUserMessage
|
|
2077
|
+
};
|
|
2078
|
+
}
|
|
2079
|
+
return {
|
|
2080
|
+
data: responseData,
|
|
2081
|
+
error: null,
|
|
2082
|
+
toolExecution: result.toolExecution,
|
|
2083
|
+
userMessage: storedUserMessage,
|
|
2084
|
+
assistantMessage: storedAssistantMessage
|
|
2085
|
+
};
|
|
2086
|
+
},
|
|
2087
|
+
[ensureConversation, getMessages, storageCtx, baseSendMessage]
|
|
2088
|
+
);
|
|
2089
|
+
const searchMessages = useCallback2(
|
|
2090
|
+
async (queryVector, options2) => {
|
|
2091
|
+
return searchMessagesOp(storageCtx, queryVector, options2);
|
|
2092
|
+
},
|
|
2093
|
+
[storageCtx]
|
|
2094
|
+
);
|
|
2095
|
+
const updateMessageEmbedding = useCallback2(
|
|
2096
|
+
async (uniqueId, vector, embeddingModel) => {
|
|
2097
|
+
return updateMessageEmbeddingOp(storageCtx, uniqueId, vector, embeddingModel);
|
|
2098
|
+
},
|
|
2099
|
+
[storageCtx]
|
|
2100
|
+
);
|
|
2101
|
+
return {
|
|
2102
|
+
isLoading,
|
|
2103
|
+
isSelectingTool,
|
|
2104
|
+
sendMessage,
|
|
2105
|
+
stop,
|
|
2106
|
+
conversationId: currentConversationId,
|
|
2107
|
+
setConversationId: setCurrentConversationId,
|
|
2108
|
+
createConversation,
|
|
2109
|
+
getConversation,
|
|
2110
|
+
getConversations,
|
|
2111
|
+
updateConversationTitle,
|
|
2112
|
+
deleteConversation,
|
|
2113
|
+
getMessages,
|
|
2114
|
+
getMessageCount,
|
|
2115
|
+
clearMessages,
|
|
2116
|
+
searchMessages,
|
|
2117
|
+
updateMessageEmbedding
|
|
2118
|
+
};
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
// src/lib/chatStorage/schema.ts
|
|
2122
|
+
import { appSchema, tableSchema } from "@nozbe/watermelondb";
|
|
2123
|
+
var chatStorageSchema = appSchema({
|
|
2124
|
+
version: 1,
|
|
2125
|
+
tables: [
|
|
2126
|
+
tableSchema({
|
|
2127
|
+
name: "history",
|
|
2128
|
+
columns: [
|
|
2129
|
+
{ name: "message_id", type: "number" },
|
|
2130
|
+
// Sequential ID within conversation
|
|
2131
|
+
{ name: "conversation_id", type: "string", isIndexed: true },
|
|
2132
|
+
{ name: "role", type: "string", isIndexed: true },
|
|
2133
|
+
// 'user' | 'assistant' | 'system'
|
|
2134
|
+
{ name: "content", type: "string" },
|
|
2135
|
+
{ name: "model", type: "string", isOptional: true },
|
|
2136
|
+
{ name: "files", type: "string", isOptional: true },
|
|
2137
|
+
// JSON stringified FileMetadata[]
|
|
2138
|
+
{ name: "created_at", type: "number", isIndexed: true },
|
|
2139
|
+
{ name: "updated_at", type: "number" },
|
|
2140
|
+
{ name: "vector", type: "string", isOptional: true },
|
|
2141
|
+
// JSON stringified number[]
|
|
2142
|
+
{ name: "embedding_model", type: "string", isOptional: true },
|
|
2143
|
+
{ name: "usage", type: "string", isOptional: true },
|
|
2144
|
+
// JSON stringified ChatCompletionUsage
|
|
2145
|
+
{ name: "sources", type: "string", isOptional: true },
|
|
2146
|
+
// JSON stringified SearchSource[]
|
|
2147
|
+
{ name: "response_duration", type: "number", isOptional: true }
|
|
2148
|
+
]
|
|
2149
|
+
}),
|
|
2150
|
+
tableSchema({
|
|
2151
|
+
name: "conversations",
|
|
2152
|
+
columns: [
|
|
2153
|
+
{ name: "conversation_id", type: "string", isIndexed: true },
|
|
2154
|
+
{ name: "title", type: "string" },
|
|
2155
|
+
{ name: "created_at", type: "number" },
|
|
2156
|
+
{ name: "updated_at", type: "number" },
|
|
2157
|
+
{ name: "is_deleted", type: "boolean", isIndexed: true }
|
|
2158
|
+
]
|
|
2159
|
+
})
|
|
2160
|
+
]
|
|
2161
|
+
});
|
|
2162
|
+
|
|
2163
|
+
// src/lib/chatStorage/models.ts
|
|
2164
|
+
import { Model } from "@nozbe/watermelondb";
|
|
2165
|
+
var Message = class extends Model {
|
|
2166
|
+
/** Sequential message ID within conversation */
|
|
2167
|
+
get messageId() {
|
|
2168
|
+
return this._getRaw("message_id");
|
|
2169
|
+
}
|
|
2170
|
+
/** Links message to its conversation */
|
|
2171
|
+
get conversationId() {
|
|
2172
|
+
return this._getRaw("conversation_id");
|
|
2173
|
+
}
|
|
2174
|
+
/** Who sent the message: 'user' | 'assistant' | 'system' */
|
|
2175
|
+
get role() {
|
|
2176
|
+
return this._getRaw("role");
|
|
2177
|
+
}
|
|
2178
|
+
/** The message text content */
|
|
2179
|
+
get content() {
|
|
2180
|
+
return this._getRaw("content");
|
|
2181
|
+
}
|
|
2182
|
+
/** LLM model used (e.g., GPT-4, Claude) */
|
|
2183
|
+
get model() {
|
|
2184
|
+
const value = this._getRaw("model");
|
|
2185
|
+
return value ? value : void 0;
|
|
2186
|
+
}
|
|
2187
|
+
/** Optional attached files */
|
|
2188
|
+
get files() {
|
|
2189
|
+
const raw = this._getRaw("files");
|
|
2190
|
+
if (!raw) return void 0;
|
|
2191
|
+
try {
|
|
2192
|
+
return JSON.parse(raw);
|
|
2193
|
+
} catch {
|
|
2194
|
+
return void 0;
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
/** Created timestamp */
|
|
2198
|
+
get createdAt() {
|
|
2199
|
+
return new Date(this._getRaw("created_at"));
|
|
2200
|
+
}
|
|
2201
|
+
/** Updated timestamp */
|
|
2202
|
+
get updatedAt() {
|
|
2203
|
+
return new Date(this._getRaw("updated_at"));
|
|
2204
|
+
}
|
|
2205
|
+
/** Embedding vector for semantic search */
|
|
2206
|
+
get vector() {
|
|
2207
|
+
const raw = this._getRaw("vector");
|
|
2208
|
+
if (!raw) return void 0;
|
|
2209
|
+
try {
|
|
2210
|
+
return JSON.parse(raw);
|
|
2211
|
+
} catch {
|
|
2212
|
+
return void 0;
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
/** Model used to generate embedding */
|
|
2216
|
+
get embeddingModel() {
|
|
2217
|
+
const value = this._getRaw("embedding_model");
|
|
2218
|
+
return value ? value : void 0;
|
|
2219
|
+
}
|
|
2220
|
+
/** Token counts and cost */
|
|
2221
|
+
get usage() {
|
|
2222
|
+
const raw = this._getRaw("usage");
|
|
2223
|
+
if (!raw) return void 0;
|
|
2224
|
+
try {
|
|
2225
|
+
return JSON.parse(raw);
|
|
2226
|
+
} catch {
|
|
2227
|
+
return void 0;
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
/** Web search sources */
|
|
2231
|
+
get sources() {
|
|
2232
|
+
const raw = this._getRaw("sources");
|
|
2233
|
+
if (!raw) return void 0;
|
|
2234
|
+
try {
|
|
2235
|
+
return JSON.parse(raw);
|
|
2236
|
+
} catch {
|
|
2237
|
+
return void 0;
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
/** Response time in seconds */
|
|
2241
|
+
get responseDuration() {
|
|
2242
|
+
const value = this._getRaw("response_duration");
|
|
2243
|
+
return value !== null && value !== void 0 ? value : void 0;
|
|
2244
|
+
}
|
|
2245
|
+
};
|
|
2246
|
+
Message.table = "history";
|
|
2247
|
+
Message.associations = {
|
|
2248
|
+
conversations: { type: "belongs_to", key: "conversation_id" }
|
|
2249
|
+
};
|
|
2250
|
+
var Conversation = class extends Model {
|
|
2251
|
+
/** Unique conversation identifier */
|
|
2252
|
+
get conversationId() {
|
|
2253
|
+
return this._getRaw("conversation_id");
|
|
2254
|
+
}
|
|
2255
|
+
/** Conversation title */
|
|
2256
|
+
get title() {
|
|
2257
|
+
return this._getRaw("title");
|
|
2258
|
+
}
|
|
2259
|
+
/** Created timestamp */
|
|
2260
|
+
get createdAt() {
|
|
2261
|
+
return new Date(this._getRaw("created_at"));
|
|
2262
|
+
}
|
|
2263
|
+
/** Updated timestamp */
|
|
2264
|
+
get updatedAt() {
|
|
2265
|
+
return new Date(this._getRaw("updated_at"));
|
|
2266
|
+
}
|
|
2267
|
+
/** Soft delete flag */
|
|
2268
|
+
get isDeleted() {
|
|
2269
|
+
return this._getRaw("is_deleted");
|
|
2270
|
+
}
|
|
2271
|
+
};
|
|
2272
|
+
Conversation.table = "conversations";
|
|
2273
|
+
Conversation.associations = {
|
|
2274
|
+
history: { type: "has_many", foreignKey: "conversation_id" }
|
|
2275
|
+
};
|
|
2276
|
+
|
|
2277
|
+
// src/react/useMemoryStorage.ts
|
|
2278
|
+
import { useCallback as useCallback3, useState as useState3, useMemo as useMemo2, useRef as useRef2 } from "react";
|
|
1627
2279
|
import { postApiV1ChatCompletions } from "@reverbia/sdk";
|
|
1628
2280
|
|
|
2281
|
+
// src/lib/memoryStorage/operations.ts
|
|
2282
|
+
import { Q as Q2 } from "@nozbe/watermelondb";
|
|
2283
|
+
|
|
2284
|
+
// src/lib/memoryStorage/types.ts
|
|
2285
|
+
function generateCompositeKey(namespace, key) {
|
|
2286
|
+
return `${namespace}:${key}`;
|
|
2287
|
+
}
|
|
2288
|
+
function generateUniqueKey(namespace, key, value) {
|
|
2289
|
+
return `${namespace}:${key}:${value}`;
|
|
2290
|
+
}
|
|
2291
|
+
function cosineSimilarity2(a, b) {
|
|
2292
|
+
if (a.length !== b.length) {
|
|
2293
|
+
throw new Error("Vectors must have the same length");
|
|
2294
|
+
}
|
|
2295
|
+
let dotProduct = 0;
|
|
2296
|
+
let normA = 0;
|
|
2297
|
+
let normB = 0;
|
|
2298
|
+
for (let i = 0; i < a.length; i++) {
|
|
2299
|
+
dotProduct += a[i] * b[i];
|
|
2300
|
+
normA += a[i] * a[i];
|
|
2301
|
+
normB += b[i] * b[i];
|
|
2302
|
+
}
|
|
2303
|
+
const denominator = Math.sqrt(normA) * Math.sqrt(normB);
|
|
2304
|
+
if (denominator === 0) {
|
|
2305
|
+
return 0;
|
|
2306
|
+
}
|
|
2307
|
+
return dotProduct / denominator;
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
// src/lib/memoryStorage/operations.ts
|
|
2311
|
+
function memoryToStored(memory) {
|
|
2312
|
+
return {
|
|
2313
|
+
uniqueId: memory.id,
|
|
2314
|
+
type: memory.type,
|
|
2315
|
+
namespace: memory.namespace,
|
|
2316
|
+
key: memory.key,
|
|
2317
|
+
value: memory.value,
|
|
2318
|
+
rawEvidence: memory.rawEvidence,
|
|
2319
|
+
confidence: memory.confidence,
|
|
2320
|
+
pii: memory.pii,
|
|
2321
|
+
compositeKey: memory.compositeKey,
|
|
2322
|
+
uniqueKey: memory.uniqueKey,
|
|
2323
|
+
createdAt: memory.createdAt,
|
|
2324
|
+
updatedAt: memory.updatedAt,
|
|
2325
|
+
embedding: memory.embedding,
|
|
2326
|
+
embeddingModel: memory.embeddingModel,
|
|
2327
|
+
isDeleted: memory.isDeleted
|
|
2328
|
+
};
|
|
2329
|
+
}
|
|
2330
|
+
async function getAllMemoriesOp(ctx) {
|
|
2331
|
+
const results = await ctx.memoriesCollection.query(Q2.where("is_deleted", false), Q2.sortBy("created_at", Q2.desc)).fetch();
|
|
2332
|
+
return results.map(memoryToStored);
|
|
2333
|
+
}
|
|
2334
|
+
async function getMemoryByIdOp(ctx, id) {
|
|
2335
|
+
try {
|
|
2336
|
+
const memory = await ctx.memoriesCollection.find(id);
|
|
2337
|
+
if (memory.isDeleted) return null;
|
|
2338
|
+
return memoryToStored(memory);
|
|
2339
|
+
} catch {
|
|
2340
|
+
return null;
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
async function getMemoriesByNamespaceOp(ctx, namespace) {
|
|
2344
|
+
const results = await ctx.memoriesCollection.query(
|
|
2345
|
+
Q2.where("namespace", namespace),
|
|
2346
|
+
Q2.where("is_deleted", false),
|
|
2347
|
+
Q2.sortBy("created_at", Q2.desc)
|
|
2348
|
+
).fetch();
|
|
2349
|
+
return results.map(memoryToStored);
|
|
2350
|
+
}
|
|
2351
|
+
async function getMemoriesByKeyOp(ctx, namespace, key) {
|
|
2352
|
+
const compositeKey = generateCompositeKey(namespace, key);
|
|
2353
|
+
const results = await ctx.memoriesCollection.query(
|
|
2354
|
+
Q2.where("composite_key", compositeKey),
|
|
2355
|
+
Q2.where("is_deleted", false),
|
|
2356
|
+
Q2.sortBy("created_at", Q2.desc)
|
|
2357
|
+
).fetch();
|
|
2358
|
+
return results.map(memoryToStored);
|
|
2359
|
+
}
|
|
2360
|
+
async function saveMemoryOp(ctx, opts) {
|
|
2361
|
+
const compositeKey = generateCompositeKey(opts.namespace, opts.key);
|
|
2362
|
+
const uniqueKey = generateUniqueKey(opts.namespace, opts.key, opts.value);
|
|
2363
|
+
const result = await ctx.database.write(async () => {
|
|
2364
|
+
const existing = await ctx.memoriesCollection.query(Q2.where("unique_key", uniqueKey)).fetch();
|
|
2365
|
+
if (existing.length > 0) {
|
|
2366
|
+
const existingMemory = existing[0];
|
|
2367
|
+
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;
|
|
2368
|
+
await existingMemory.update((mem) => {
|
|
2369
|
+
mem._setRaw("type", opts.type);
|
|
2370
|
+
mem._setRaw("namespace", opts.namespace);
|
|
2371
|
+
mem._setRaw("key", opts.key);
|
|
2372
|
+
mem._setRaw("value", opts.value);
|
|
2373
|
+
mem._setRaw("raw_evidence", opts.rawEvidence);
|
|
2374
|
+
mem._setRaw("confidence", opts.confidence);
|
|
2375
|
+
mem._setRaw("pii", opts.pii);
|
|
2376
|
+
mem._setRaw("composite_key", compositeKey);
|
|
2377
|
+
mem._setRaw("unique_key", uniqueKey);
|
|
2378
|
+
mem._setRaw("is_deleted", false);
|
|
2379
|
+
if (shouldPreserveEmbedding) {
|
|
2380
|
+
} else if (opts.embedding) {
|
|
2381
|
+
mem._setRaw("embedding", JSON.stringify(opts.embedding));
|
|
2382
|
+
if (opts.embeddingModel) {
|
|
2383
|
+
mem._setRaw("embedding_model", opts.embeddingModel);
|
|
2384
|
+
}
|
|
2385
|
+
} else {
|
|
2386
|
+
mem._setRaw("embedding", null);
|
|
2387
|
+
mem._setRaw("embedding_model", null);
|
|
2388
|
+
}
|
|
2389
|
+
});
|
|
2390
|
+
return existingMemory;
|
|
2391
|
+
}
|
|
2392
|
+
return await ctx.memoriesCollection.create((mem) => {
|
|
2393
|
+
mem._setRaw("type", opts.type);
|
|
2394
|
+
mem._setRaw("namespace", opts.namespace);
|
|
2395
|
+
mem._setRaw("key", opts.key);
|
|
2396
|
+
mem._setRaw("value", opts.value);
|
|
2397
|
+
mem._setRaw("raw_evidence", opts.rawEvidence);
|
|
2398
|
+
mem._setRaw("confidence", opts.confidence);
|
|
2399
|
+
mem._setRaw("pii", opts.pii);
|
|
2400
|
+
mem._setRaw("composite_key", compositeKey);
|
|
2401
|
+
mem._setRaw("unique_key", uniqueKey);
|
|
2402
|
+
mem._setRaw("is_deleted", false);
|
|
2403
|
+
if (opts.embedding) {
|
|
2404
|
+
mem._setRaw("embedding", JSON.stringify(opts.embedding));
|
|
2405
|
+
if (opts.embeddingModel) {
|
|
2406
|
+
mem._setRaw("embedding_model", opts.embeddingModel);
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2409
|
+
});
|
|
2410
|
+
});
|
|
2411
|
+
return memoryToStored(result);
|
|
2412
|
+
}
|
|
2413
|
+
async function saveMemoriesOp(ctx, memories) {
|
|
2414
|
+
const results = [];
|
|
2415
|
+
for (const memory of memories) {
|
|
2416
|
+
const saved = await saveMemoryOp(ctx, memory);
|
|
2417
|
+
results.push(saved);
|
|
2418
|
+
}
|
|
2419
|
+
return results;
|
|
2420
|
+
}
|
|
2421
|
+
async function updateMemoryOp(ctx, id, updates) {
|
|
2422
|
+
let memory;
|
|
2423
|
+
try {
|
|
2424
|
+
memory = await ctx.memoriesCollection.find(id);
|
|
2425
|
+
} catch {
|
|
2426
|
+
return { ok: false, reason: "not_found" };
|
|
2427
|
+
}
|
|
2428
|
+
if (memory.isDeleted) {
|
|
2429
|
+
return { ok: false, reason: "not_found" };
|
|
2430
|
+
}
|
|
2431
|
+
const newNamespace = updates.namespace ?? memory.namespace;
|
|
2432
|
+
const newKey = updates.key ?? memory.key;
|
|
2433
|
+
const newValue = updates.value ?? memory.value;
|
|
2434
|
+
const newCompositeKey = generateCompositeKey(newNamespace, newKey);
|
|
2435
|
+
const newUniqueKey = generateUniqueKey(newNamespace, newKey, newValue);
|
|
2436
|
+
if (newUniqueKey !== memory.uniqueKey) {
|
|
2437
|
+
const existing = await ctx.memoriesCollection.query(Q2.where("unique_key", newUniqueKey), Q2.where("is_deleted", false)).fetch();
|
|
2438
|
+
if (existing.length > 0) {
|
|
2439
|
+
return { ok: false, reason: "conflict", conflictingKey: newUniqueKey };
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
try {
|
|
2443
|
+
const updated = await ctx.database.write(async () => {
|
|
2444
|
+
await memory.update((mem) => {
|
|
2445
|
+
if (updates.type !== void 0) mem._setRaw("type", updates.type);
|
|
2446
|
+
if (updates.namespace !== void 0)
|
|
2447
|
+
mem._setRaw("namespace", updates.namespace);
|
|
2448
|
+
if (updates.key !== void 0) mem._setRaw("key", updates.key);
|
|
2449
|
+
if (updates.value !== void 0) mem._setRaw("value", updates.value);
|
|
2450
|
+
if (updates.rawEvidence !== void 0)
|
|
2451
|
+
mem._setRaw("raw_evidence", updates.rawEvidence);
|
|
2452
|
+
if (updates.confidence !== void 0)
|
|
2453
|
+
mem._setRaw("confidence", updates.confidence);
|
|
2454
|
+
if (updates.pii !== void 0) mem._setRaw("pii", updates.pii);
|
|
2455
|
+
if (updates.namespace !== void 0 || updates.key !== void 0 || updates.value !== void 0) {
|
|
2456
|
+
mem._setRaw("composite_key", newCompositeKey);
|
|
2457
|
+
mem._setRaw("unique_key", newUniqueKey);
|
|
2458
|
+
}
|
|
2459
|
+
if (updates.embedding !== void 0) {
|
|
2460
|
+
mem._setRaw(
|
|
2461
|
+
"embedding",
|
|
2462
|
+
updates.embedding ? JSON.stringify(updates.embedding) : null
|
|
2463
|
+
);
|
|
2464
|
+
}
|
|
2465
|
+
if (updates.embeddingModel !== void 0) {
|
|
2466
|
+
mem._setRaw("embedding_model", updates.embeddingModel || null);
|
|
2467
|
+
}
|
|
2468
|
+
});
|
|
2469
|
+
return memory;
|
|
2470
|
+
});
|
|
2471
|
+
return { ok: true, memory: memoryToStored(updated) };
|
|
2472
|
+
} catch (err) {
|
|
2473
|
+
return {
|
|
2474
|
+
ok: false,
|
|
2475
|
+
reason: "error",
|
|
2476
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
2477
|
+
};
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
async function deleteMemoryByIdOp(ctx, id) {
|
|
2481
|
+
try {
|
|
2482
|
+
const memory = await ctx.memoriesCollection.find(id);
|
|
2483
|
+
await ctx.database.write(async () => {
|
|
2484
|
+
await memory.update((mem) => {
|
|
2485
|
+
mem._setRaw("is_deleted", true);
|
|
2486
|
+
});
|
|
2487
|
+
});
|
|
2488
|
+
} catch {
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
async function deleteMemoryOp(ctx, namespace, key, value) {
|
|
2492
|
+
const uniqueKey = generateUniqueKey(namespace, key, value);
|
|
2493
|
+
const results = await ctx.memoriesCollection.query(Q2.where("unique_key", uniqueKey)).fetch();
|
|
2494
|
+
if (results.length > 0) {
|
|
2495
|
+
await ctx.database.write(async () => {
|
|
2496
|
+
await results[0].update((mem) => {
|
|
2497
|
+
mem._setRaw("is_deleted", true);
|
|
2498
|
+
});
|
|
2499
|
+
});
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
async function deleteMemoriesByKeyOp(ctx, namespace, key) {
|
|
2503
|
+
const compositeKey = generateCompositeKey(namespace, key);
|
|
2504
|
+
const results = await ctx.memoriesCollection.query(Q2.where("composite_key", compositeKey), Q2.where("is_deleted", false)).fetch();
|
|
2505
|
+
await ctx.database.write(async () => {
|
|
2506
|
+
for (const memory of results) {
|
|
2507
|
+
await memory.update((mem) => {
|
|
2508
|
+
mem._setRaw("is_deleted", true);
|
|
2509
|
+
});
|
|
2510
|
+
}
|
|
2511
|
+
});
|
|
2512
|
+
}
|
|
2513
|
+
async function clearAllMemoriesOp(ctx) {
|
|
2514
|
+
const results = await ctx.memoriesCollection.query(Q2.where("is_deleted", false)).fetch();
|
|
2515
|
+
await ctx.database.write(async () => {
|
|
2516
|
+
for (const memory of results) {
|
|
2517
|
+
await memory.update((mem) => {
|
|
2518
|
+
mem._setRaw("is_deleted", true);
|
|
2519
|
+
});
|
|
2520
|
+
}
|
|
2521
|
+
});
|
|
2522
|
+
}
|
|
2523
|
+
async function searchSimilarMemoriesOp(ctx, queryEmbedding, limit = 10, minSimilarity = 0.6) {
|
|
2524
|
+
const allMemories = await ctx.memoriesCollection.query(Q2.where("is_deleted", false)).fetch();
|
|
2525
|
+
const memoriesWithEmbeddings = allMemories.filter(
|
|
2526
|
+
(m) => m.embedding && m.embedding.length > 0
|
|
2527
|
+
);
|
|
2528
|
+
if (memoriesWithEmbeddings.length === 0) {
|
|
2529
|
+
return [];
|
|
2530
|
+
}
|
|
2531
|
+
const results = memoriesWithEmbeddings.map((memory) => {
|
|
2532
|
+
const similarity = cosineSimilarity2(queryEmbedding, memory.embedding);
|
|
2533
|
+
return {
|
|
2534
|
+
...memoryToStored(memory),
|
|
2535
|
+
similarity
|
|
2536
|
+
};
|
|
2537
|
+
}).filter((result) => result.similarity >= minSimilarity).sort((a, b) => b.similarity - a.similarity).slice(0, limit);
|
|
2538
|
+
return results;
|
|
2539
|
+
}
|
|
2540
|
+
async function updateMemoryEmbeddingOp(ctx, id, embedding, embeddingModel) {
|
|
2541
|
+
try {
|
|
2542
|
+
const memory = await ctx.memoriesCollection.find(id);
|
|
2543
|
+
await ctx.database.write(async () => {
|
|
2544
|
+
await memory.update((mem) => {
|
|
2545
|
+
mem._setRaw("embedding", JSON.stringify(embedding));
|
|
2546
|
+
mem._setRaw("embedding_model", embeddingModel);
|
|
2547
|
+
});
|
|
2548
|
+
});
|
|
2549
|
+
} catch {
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
|
|
1629
2553
|
// src/lib/memory/service.ts
|
|
1630
2554
|
var FACT_EXTRACTION_PROMPT = `You are a memory extraction system. Extract durable user memories from chat messages.
|
|
1631
2555
|
|
|
@@ -1755,180 +2679,6 @@ var preprocessMemories = (items, minConfidence = 0.6) => {
|
|
|
1755
2679
|
return Array.from(deduplicatedMap.values());
|
|
1756
2680
|
};
|
|
1757
2681
|
|
|
1758
|
-
// src/lib/memory/db.ts
|
|
1759
|
-
import Dexie from "dexie";
|
|
1760
|
-
var MemoryDatabase = class extends Dexie {
|
|
1761
|
-
constructor() {
|
|
1762
|
-
super("MemoryDatabase");
|
|
1763
|
-
this.version(2).stores({
|
|
1764
|
-
memories: "++id, uniqueKey, compositeKey, namespace, key, type, createdAt, updatedAt"
|
|
1765
|
-
});
|
|
1766
|
-
this.version(3).stores({
|
|
1767
|
-
memories: "++id, uniqueKey, compositeKey, namespace, key, type, createdAt, updatedAt"
|
|
1768
|
-
});
|
|
1769
|
-
}
|
|
1770
|
-
};
|
|
1771
|
-
var memoryDb = new MemoryDatabase();
|
|
1772
|
-
var saveMemory = async (memory) => {
|
|
1773
|
-
const compositeKey = `${memory.namespace}:${memory.key}`;
|
|
1774
|
-
const uniqueKey = `${memory.namespace}:${memory.key}:${memory.value}`;
|
|
1775
|
-
const now = Date.now();
|
|
1776
|
-
const existing = await memoryDb.memories.where("uniqueKey").equals(uniqueKey).first();
|
|
1777
|
-
if (existing) {
|
|
1778
|
-
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;
|
|
1779
|
-
const updateData = {
|
|
1780
|
-
...memory,
|
|
1781
|
-
compositeKey,
|
|
1782
|
-
uniqueKey,
|
|
1783
|
-
updatedAt: now,
|
|
1784
|
-
createdAt: existing.createdAt
|
|
1785
|
-
};
|
|
1786
|
-
if (shouldPreserveEmbedding) {
|
|
1787
|
-
updateData.embedding = existing.embedding;
|
|
1788
|
-
updateData.embeddingModel = existing.embeddingModel;
|
|
1789
|
-
} else {
|
|
1790
|
-
updateData.embedding = [];
|
|
1791
|
-
updateData.embeddingModel = void 0;
|
|
1792
|
-
}
|
|
1793
|
-
await memoryDb.memories.update(existing.id, updateData);
|
|
1794
|
-
} else {
|
|
1795
|
-
await memoryDb.memories.add({
|
|
1796
|
-
...memory,
|
|
1797
|
-
compositeKey,
|
|
1798
|
-
uniqueKey,
|
|
1799
|
-
createdAt: now,
|
|
1800
|
-
updatedAt: now
|
|
1801
|
-
});
|
|
1802
|
-
}
|
|
1803
|
-
};
|
|
1804
|
-
var saveMemories = async (memories) => {
|
|
1805
|
-
await Promise.all(memories.map((memory) => saveMemory(memory)));
|
|
1806
|
-
};
|
|
1807
|
-
var updateMemoryById = async (id, updates, existingMemory, embedding, embeddingModel) => {
|
|
1808
|
-
const now = Date.now();
|
|
1809
|
-
const updatedMemory = {
|
|
1810
|
-
...updates,
|
|
1811
|
-
updatedAt: now
|
|
1812
|
-
};
|
|
1813
|
-
if ("namespace" in updates || "key" in updates || "value" in updates) {
|
|
1814
|
-
const namespace = updates.namespace ?? existingMemory.namespace;
|
|
1815
|
-
const key = updates.key ?? existingMemory.key;
|
|
1816
|
-
const value = updates.value ?? existingMemory.value;
|
|
1817
|
-
const newUniqueKey = `${namespace}:${key}:${value}`;
|
|
1818
|
-
if (newUniqueKey !== existingMemory.uniqueKey) {
|
|
1819
|
-
const conflicting = await getMemory(namespace, key, value);
|
|
1820
|
-
if (conflicting) {
|
|
1821
|
-
throw new Error(
|
|
1822
|
-
`A memory with uniqueKey "${newUniqueKey}" already exists (id: ${conflicting.id})`
|
|
1823
|
-
);
|
|
1824
|
-
}
|
|
1825
|
-
}
|
|
1826
|
-
updatedMemory.compositeKey = `${namespace}:${key}`;
|
|
1827
|
-
updatedMemory.uniqueKey = newUniqueKey;
|
|
1828
|
-
}
|
|
1829
|
-
updatedMemory.embedding = embedding;
|
|
1830
|
-
updatedMemory.embeddingModel = embeddingModel;
|
|
1831
|
-
return await memoryDb.transaction("rw", memoryDb.memories, async () => {
|
|
1832
|
-
await memoryDb.memories.update(id, updatedMemory);
|
|
1833
|
-
return await memoryDb.memories.get(id);
|
|
1834
|
-
});
|
|
1835
|
-
};
|
|
1836
|
-
var getAllMemories = async () => {
|
|
1837
|
-
return memoryDb.memories.toArray();
|
|
1838
|
-
};
|
|
1839
|
-
var getMemoryById = async (id) => {
|
|
1840
|
-
return memoryDb.memories.get(id);
|
|
1841
|
-
};
|
|
1842
|
-
var getMemoriesByNamespace = async (namespace) => {
|
|
1843
|
-
return memoryDb.memories.where("namespace").equals(namespace).toArray();
|
|
1844
|
-
};
|
|
1845
|
-
var getMemories = async (namespace, key) => {
|
|
1846
|
-
const compositeKey = `${namespace}:${key}`;
|
|
1847
|
-
return memoryDb.memories.where("compositeKey").equals(compositeKey).toArray();
|
|
1848
|
-
};
|
|
1849
|
-
var getMemory = async (namespace, key, value) => {
|
|
1850
|
-
const uniqueKey = `${namespace}:${key}:${value}`;
|
|
1851
|
-
return memoryDb.memories.where("uniqueKey").equals(uniqueKey).first();
|
|
1852
|
-
};
|
|
1853
|
-
var deleteMemories = async (namespace, key) => {
|
|
1854
|
-
const compositeKey = `${namespace}:${key}`;
|
|
1855
|
-
await memoryDb.memories.where("compositeKey").equals(compositeKey).delete();
|
|
1856
|
-
};
|
|
1857
|
-
var deleteMemory = async (namespace, key, value) => {
|
|
1858
|
-
const uniqueKey = `${namespace}:${key}:${value}`;
|
|
1859
|
-
const existing = await memoryDb.memories.where("uniqueKey").equals(uniqueKey).first();
|
|
1860
|
-
if (existing?.id) {
|
|
1861
|
-
await memoryDb.memories.delete(existing.id);
|
|
1862
|
-
}
|
|
1863
|
-
};
|
|
1864
|
-
var deleteMemoryById = async (id) => {
|
|
1865
|
-
await memoryDb.memories.delete(id);
|
|
1866
|
-
};
|
|
1867
|
-
var clearAllMemories = async () => {
|
|
1868
|
-
await memoryDb.memories.clear();
|
|
1869
|
-
};
|
|
1870
|
-
var cosineSimilarity = (a, b) => {
|
|
1871
|
-
if (a.length !== b.length) {
|
|
1872
|
-
throw new Error("Vectors must have the same length");
|
|
1873
|
-
}
|
|
1874
|
-
let dotProduct = 0;
|
|
1875
|
-
let normA = 0;
|
|
1876
|
-
let normB = 0;
|
|
1877
|
-
for (let i = 0; i < a.length; i++) {
|
|
1878
|
-
dotProduct += a[i] * b[i];
|
|
1879
|
-
normA += a[i] * a[i];
|
|
1880
|
-
normB += b[i] * b[i];
|
|
1881
|
-
}
|
|
1882
|
-
const denominator = Math.sqrt(normA) * Math.sqrt(normB);
|
|
1883
|
-
if (denominator === 0) {
|
|
1884
|
-
return 0;
|
|
1885
|
-
}
|
|
1886
|
-
return dotProduct / denominator;
|
|
1887
|
-
};
|
|
1888
|
-
var searchSimilarMemories = async (queryEmbedding, limit = 10, minSimilarity = 0.6) => {
|
|
1889
|
-
const allMemories = await getAllMemories();
|
|
1890
|
-
const memoriesWithEmbeddings = allMemories.filter(
|
|
1891
|
-
(m) => m.embedding && m.embedding.length > 0
|
|
1892
|
-
);
|
|
1893
|
-
console.log(
|
|
1894
|
-
`[Memory Search] Total memories: ${allMemories.length}, memories with embeddings: ${memoriesWithEmbeddings.length}`
|
|
1895
|
-
);
|
|
1896
|
-
if (memoriesWithEmbeddings.length === 0) {
|
|
1897
|
-
console.warn(
|
|
1898
|
-
"[Memory Search] No memories with embeddings found. Memories may need embeddings generated. Use generateAndStoreEmbeddings() to generate embeddings for existing memories."
|
|
1899
|
-
);
|
|
1900
|
-
return [];
|
|
1901
|
-
}
|
|
1902
|
-
const allResults = memoriesWithEmbeddings.map((memory) => {
|
|
1903
|
-
const similarity = cosineSimilarity(queryEmbedding, memory.embedding);
|
|
1904
|
-
return {
|
|
1905
|
-
...memory,
|
|
1906
|
-
similarity
|
|
1907
|
-
};
|
|
1908
|
-
}).sort((a, b) => b.similarity - a.similarity);
|
|
1909
|
-
console.log(
|
|
1910
|
-
`[Memory Search] All similarity scores:`,
|
|
1911
|
-
allResults.map((r) => ({
|
|
1912
|
-
key: `${r.namespace}:${r.key}`,
|
|
1913
|
-
value: r.value,
|
|
1914
|
-
similarity: r.similarity.toFixed(4)
|
|
1915
|
-
}))
|
|
1916
|
-
);
|
|
1917
|
-
const results = allResults.filter((result) => result.similarity >= minSimilarity).slice(0, limit);
|
|
1918
|
-
if (results.length === 0 && allResults.length > 0) {
|
|
1919
|
-
const topSimilarity = allResults[0].similarity;
|
|
1920
|
-
const suggestedThreshold = Math.max(0.3, topSimilarity - 0.1);
|
|
1921
|
-
console.warn(
|
|
1922
|
-
`[Memory Search] No memories above threshold ${minSimilarity}. Highest similarity was ${topSimilarity.toFixed(4)}. Consider lowering the threshold to ${suggestedThreshold.toFixed(2)}`
|
|
1923
|
-
);
|
|
1924
|
-
} else {
|
|
1925
|
-
console.log(
|
|
1926
|
-
`[Memory Search] Found ${results.length} memories above similarity threshold ${minSimilarity}. Top similarity: ${results[0]?.similarity.toFixed(4) || "N/A"}`
|
|
1927
|
-
);
|
|
1928
|
-
}
|
|
1929
|
-
return results;
|
|
1930
|
-
};
|
|
1931
|
-
|
|
1932
2682
|
// src/client/sdk.gen.ts
|
|
1933
2683
|
var postApiV1Embeddings = (options) => {
|
|
1934
2684
|
return (options.client ?? client).post({
|
|
@@ -2037,70 +2787,11 @@ var generateEmbeddingForMemory = async (memory, options = {}) => {
|
|
|
2037
2787
|
].filter(Boolean).join(" ");
|
|
2038
2788
|
return generateEmbeddingForText(text, options);
|
|
2039
2789
|
};
|
|
2040
|
-
var generateEmbeddingsForMemories = async (memories, options = {}) => {
|
|
2041
|
-
const embeddings = /* @__PURE__ */ new Map();
|
|
2042
|
-
for (const memory of memories) {
|
|
2043
|
-
const uniqueKey = `${memory.namespace}:${memory.key}:${memory.value}`;
|
|
2044
|
-
try {
|
|
2045
|
-
const embedding = await generateEmbeddingForMemory(memory, options);
|
|
2046
|
-
embeddings.set(uniqueKey, embedding);
|
|
2047
|
-
} catch (error) {
|
|
2048
|
-
console.error(
|
|
2049
|
-
`Failed to generate embedding for memory ${uniqueKey}:`,
|
|
2050
|
-
error
|
|
2051
|
-
);
|
|
2052
|
-
}
|
|
2053
|
-
}
|
|
2054
|
-
return embeddings;
|
|
2055
|
-
};
|
|
2056
|
-
var updateMemoriesWithEmbeddings = async (embeddings, embeddingModel) => {
|
|
2057
|
-
const updates = Array.from(embeddings.entries()).map(
|
|
2058
|
-
async ([uniqueKey, embedding]) => {
|
|
2059
|
-
const existing = await memoryDb.memories.where("uniqueKey").equals(uniqueKey).first();
|
|
2060
|
-
if (existing?.id) {
|
|
2061
|
-
await memoryDb.memories.update(existing.id, {
|
|
2062
|
-
embedding,
|
|
2063
|
-
embeddingModel,
|
|
2064
|
-
updatedAt: Date.now(),
|
|
2065
|
-
createdAt: existing.createdAt
|
|
2066
|
-
});
|
|
2067
|
-
} else {
|
|
2068
|
-
console.warn(
|
|
2069
|
-
`[Embeddings] Memory with uniqueKey ${uniqueKey} not found. It may have been updated or deleted before embedding was generated.`
|
|
2070
|
-
);
|
|
2071
|
-
}
|
|
2072
|
-
}
|
|
2073
|
-
);
|
|
2074
|
-
await Promise.all(updates);
|
|
2075
|
-
};
|
|
2076
|
-
var generateAndStoreEmbeddings = async (memories, options = {}) => {
|
|
2077
|
-
let { model } = options;
|
|
2078
|
-
const { provider = "local" } = options;
|
|
2079
|
-
if (!model) {
|
|
2080
|
-
if (provider === "local") {
|
|
2081
|
-
model = DEFAULT_LOCAL_EMBEDDING_MODEL;
|
|
2082
|
-
} else {
|
|
2083
|
-
model = DEFAULT_API_EMBEDDING_MODEL;
|
|
2084
|
-
}
|
|
2085
|
-
}
|
|
2086
|
-
if (provider === "local" && model === DEFAULT_API_EMBEDDING_MODEL) {
|
|
2087
|
-
model = DEFAULT_LOCAL_EMBEDDING_MODEL;
|
|
2088
|
-
}
|
|
2089
|
-
if (memories.length === 0) {
|
|
2090
|
-
return;
|
|
2091
|
-
}
|
|
2092
|
-
console.log(`Generating embeddings for ${memories.length} memories...`);
|
|
2093
|
-
const embeddings = await generateEmbeddingsForMemories(memories, {
|
|
2094
|
-
...options,
|
|
2095
|
-
model
|
|
2096
|
-
});
|
|
2097
|
-
await updateMemoriesWithEmbeddings(embeddings, model);
|
|
2098
|
-
console.log(`Generated and stored ${embeddings.size} embeddings`);
|
|
2099
|
-
};
|
|
2100
2790
|
|
|
2101
|
-
// src/react/
|
|
2102
|
-
function
|
|
2791
|
+
// src/react/useMemoryStorage.ts
|
|
2792
|
+
function useMemoryStorage(options) {
|
|
2103
2793
|
const {
|
|
2794
|
+
database,
|
|
2104
2795
|
completionsModel = DEFAULT_COMPLETION_MODEL,
|
|
2105
2796
|
embeddingModel: userEmbeddingModel,
|
|
2106
2797
|
embeddingProvider = "local",
|
|
@@ -2110,10 +2801,39 @@ function useMemory(options = {}) {
|
|
|
2110
2801
|
baseUrl = BASE_URL
|
|
2111
2802
|
} = options;
|
|
2112
2803
|
const embeddingModel = userEmbeddingModel === void 0 ? embeddingProvider === "local" ? DEFAULT_LOCAL_EMBEDDING_MODEL : DEFAULT_API_EMBEDDING_MODEL : userEmbeddingModel;
|
|
2804
|
+
const [memories, setMemories] = useState3([]);
|
|
2113
2805
|
const extractionInProgressRef = useRef2(false);
|
|
2114
|
-
const
|
|
2115
|
-
|
|
2116
|
-
|
|
2806
|
+
const memoriesCollection = useMemo2(
|
|
2807
|
+
() => database.get("memories"),
|
|
2808
|
+
[database]
|
|
2809
|
+
);
|
|
2810
|
+
const storageCtx = useMemo2(
|
|
2811
|
+
() => ({
|
|
2812
|
+
database,
|
|
2813
|
+
memoriesCollection
|
|
2814
|
+
}),
|
|
2815
|
+
[database, memoriesCollection]
|
|
2816
|
+
);
|
|
2817
|
+
const effectiveEmbeddingModel = useMemo2(
|
|
2818
|
+
() => embeddingModel ?? (embeddingProvider === "api" ? DEFAULT_API_EMBEDDING_MODEL : DEFAULT_LOCAL_EMBEDDING_MODEL),
|
|
2819
|
+
[embeddingModel, embeddingProvider]
|
|
2820
|
+
);
|
|
2821
|
+
const embeddingOptions = useMemo2(
|
|
2822
|
+
() => ({
|
|
2823
|
+
model: effectiveEmbeddingModel,
|
|
2824
|
+
provider: embeddingProvider,
|
|
2825
|
+
getToken: getToken || void 0,
|
|
2826
|
+
baseUrl
|
|
2827
|
+
}),
|
|
2828
|
+
[effectiveEmbeddingModel, embeddingProvider, getToken, baseUrl]
|
|
2829
|
+
);
|
|
2830
|
+
const refreshMemories = useCallback3(async () => {
|
|
2831
|
+
const storedMemories = await getAllMemoriesOp(storageCtx);
|
|
2832
|
+
setMemories(storedMemories);
|
|
2833
|
+
}, [storageCtx]);
|
|
2834
|
+
const extractMemoriesFromMessage = useCallback3(
|
|
2835
|
+
async (opts) => {
|
|
2836
|
+
const { messages, model } = opts;
|
|
2117
2837
|
if (!getToken || extractionInProgressRef.current) {
|
|
2118
2838
|
return null;
|
|
2119
2839
|
}
|
|
@@ -2199,11 +2919,7 @@ function useMemory(options = {}) {
|
|
|
2199
2919
|
if (jsonObjectMatch && jsonObjectMatch[0]) {
|
|
2200
2920
|
jsonContent = jsonObjectMatch[0];
|
|
2201
2921
|
} else {
|
|
2202
|
-
console.warn(
|
|
2203
|
-
"Memory extraction returned non-JSON response. The model may not have found any memories to extract, or it returned natural language instead of JSON.",
|
|
2204
|
-
"\nFirst 200 chars of response:",
|
|
2205
|
-
content.substring(0, 200)
|
|
2206
|
-
);
|
|
2922
|
+
console.warn("Memory extraction returned non-JSON response");
|
|
2207
2923
|
return { items: [] };
|
|
2208
2924
|
}
|
|
2209
2925
|
}
|
|
@@ -2211,9 +2927,7 @@ function useMemory(options = {}) {
|
|
|
2211
2927
|
const trimmedJson = jsonContent.trim();
|
|
2212
2928
|
if (!trimmedJson.startsWith("{") || !trimmedJson.includes("items")) {
|
|
2213
2929
|
console.warn(
|
|
2214
|
-
"Memory extraction response doesn't appear to be valid JSON
|
|
2215
|
-
"\nResponse preview:",
|
|
2216
|
-
content.substring(0, 200)
|
|
2930
|
+
"Memory extraction response doesn't appear to be valid JSON"
|
|
2217
2931
|
);
|
|
2218
2932
|
return { items: [] };
|
|
2219
2933
|
}
|
|
@@ -2224,10 +2938,7 @@ function useMemory(options = {}) {
|
|
|
2224
2938
|
throw new Error("Invalid JSON structure: not an object");
|
|
2225
2939
|
}
|
|
2226
2940
|
if (!Array.isArray(result.items)) {
|
|
2227
|
-
console.warn(
|
|
2228
|
-
"Memory extraction result missing 'items' array. Result:",
|
|
2229
|
-
result
|
|
2230
|
-
);
|
|
2941
|
+
console.warn("Memory extraction result missing 'items' array");
|
|
2231
2942
|
return { items: [] };
|
|
2232
2943
|
}
|
|
2233
2944
|
} catch (parseError) {
|
|
@@ -2235,42 +2946,61 @@ function useMemory(options = {}) {
|
|
|
2235
2946
|
"Failed to parse memory extraction JSON:",
|
|
2236
2947
|
parseError instanceof Error ? parseError.message : parseError
|
|
2237
2948
|
);
|
|
2238
|
-
console.error("Attempted to parse:", jsonContent.substring(0, 200));
|
|
2239
|
-
console.error("Full raw content:", content.substring(0, 500));
|
|
2240
2949
|
return { items: [] };
|
|
2241
2950
|
}
|
|
2242
2951
|
if (result.items && Array.isArray(result.items)) {
|
|
2243
|
-
const originalCount = result.items.length;
|
|
2244
2952
|
result.items = preprocessMemories(result.items);
|
|
2245
|
-
const filteredCount = result.items.length;
|
|
2246
|
-
if (originalCount !== filteredCount) {
|
|
2247
|
-
console.log(
|
|
2248
|
-
`Preprocessed memories: ${originalCount} -> ${filteredCount} (dropped ${originalCount - filteredCount} entries)`
|
|
2249
|
-
);
|
|
2250
|
-
}
|
|
2251
2953
|
}
|
|
2252
|
-
console.log("Extracted memories:", JSON.stringify(result, null, 2));
|
|
2253
2954
|
if (result.items && result.items.length > 0) {
|
|
2254
2955
|
try {
|
|
2255
|
-
|
|
2256
|
-
|
|
2956
|
+
const createOptions = result.items.map(
|
|
2957
|
+
(item) => ({
|
|
2958
|
+
type: item.type,
|
|
2959
|
+
namespace: item.namespace,
|
|
2960
|
+
key: item.key,
|
|
2961
|
+
value: item.value,
|
|
2962
|
+
rawEvidence: item.rawEvidence,
|
|
2963
|
+
confidence: item.confidence,
|
|
2964
|
+
pii: item.pii
|
|
2965
|
+
})
|
|
2966
|
+
);
|
|
2967
|
+
const savedMemories = await saveMemoriesOp(storageCtx, createOptions);
|
|
2968
|
+
console.log(
|
|
2969
|
+
`Saved ${savedMemories.length} memories to WatermelonDB`
|
|
2970
|
+
);
|
|
2257
2971
|
if (generateEmbeddings && embeddingModel) {
|
|
2258
2972
|
try {
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2973
|
+
for (const saved of savedMemories) {
|
|
2974
|
+
const memoryItem = {
|
|
2975
|
+
type: saved.type,
|
|
2976
|
+
namespace: saved.namespace,
|
|
2977
|
+
key: saved.key,
|
|
2978
|
+
value: saved.value,
|
|
2979
|
+
rawEvidence: saved.rawEvidence,
|
|
2980
|
+
confidence: saved.confidence,
|
|
2981
|
+
pii: saved.pii
|
|
2982
|
+
};
|
|
2983
|
+
const embedding = await generateEmbeddingForMemory(
|
|
2984
|
+
memoryItem,
|
|
2985
|
+
embeddingOptions
|
|
2986
|
+
);
|
|
2987
|
+
await updateMemoryEmbeddingOp(
|
|
2988
|
+
storageCtx,
|
|
2989
|
+
saved.uniqueId,
|
|
2990
|
+
embedding,
|
|
2991
|
+
effectiveEmbeddingModel
|
|
2992
|
+
);
|
|
2993
|
+
}
|
|
2265
2994
|
console.log(
|
|
2266
|
-
`Generated embeddings for ${
|
|
2995
|
+
`Generated embeddings for ${savedMemories.length} memories`
|
|
2267
2996
|
);
|
|
2268
2997
|
} catch (error) {
|
|
2269
2998
|
console.error("Failed to generate embeddings:", error);
|
|
2270
2999
|
}
|
|
2271
3000
|
}
|
|
3001
|
+
await refreshMemories();
|
|
2272
3002
|
} catch (error) {
|
|
2273
|
-
console.error("Failed to save memories to
|
|
3003
|
+
console.error("Failed to save memories to WatermelonDB:", error);
|
|
2274
3004
|
}
|
|
2275
3005
|
}
|
|
2276
3006
|
if (onFactsExtracted) {
|
|
@@ -2287,38 +3017,35 @@ function useMemory(options = {}) {
|
|
|
2287
3017
|
[
|
|
2288
3018
|
completionsModel,
|
|
2289
3019
|
embeddingModel,
|
|
2290
|
-
|
|
3020
|
+
embeddingOptions,
|
|
2291
3021
|
generateEmbeddings,
|
|
2292
3022
|
getToken,
|
|
2293
3023
|
onFactsExtracted,
|
|
2294
|
-
baseUrl
|
|
3024
|
+
baseUrl,
|
|
3025
|
+
storageCtx,
|
|
3026
|
+
refreshMemories
|
|
2295
3027
|
]
|
|
2296
3028
|
);
|
|
2297
|
-
const searchMemories =
|
|
3029
|
+
const searchMemories = useCallback3(
|
|
2298
3030
|
async (query, limit = 10, minSimilarity = 0.6) => {
|
|
2299
3031
|
if (!embeddingModel) {
|
|
2300
3032
|
console.warn("Cannot search memories: embeddingModel not provided");
|
|
2301
3033
|
return [];
|
|
2302
3034
|
}
|
|
2303
3035
|
try {
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
provider: embeddingProvider,
|
|
2308
|
-
getToken,
|
|
2309
|
-
baseUrl
|
|
2310
|
-
});
|
|
2311
|
-
console.log(
|
|
2312
|
-
`[Memory Search] Generated query embedding (${queryEmbedding.length} dimensions)`
|
|
3036
|
+
const queryEmbedding = await generateEmbeddingForText(
|
|
3037
|
+
query,
|
|
3038
|
+
embeddingOptions
|
|
2313
3039
|
);
|
|
2314
|
-
const results = await
|
|
3040
|
+
const results = await searchSimilarMemoriesOp(
|
|
3041
|
+
storageCtx,
|
|
2315
3042
|
queryEmbedding,
|
|
2316
3043
|
limit,
|
|
2317
3044
|
minSimilarity
|
|
2318
3045
|
);
|
|
2319
3046
|
if (results.length === 0) {
|
|
2320
3047
|
console.warn(
|
|
2321
|
-
`[Memory Search] No memories found above similarity threshold ${minSimilarity}
|
|
3048
|
+
`[Memory Search] No memories found above similarity threshold ${minSimilarity}.`
|
|
2322
3049
|
);
|
|
2323
3050
|
} else {
|
|
2324
3051
|
console.log(
|
|
@@ -2326,175 +3053,282 @@ function useMemory(options = {}) {
|
|
|
2326
3053
|
);
|
|
2327
3054
|
}
|
|
2328
3055
|
return results;
|
|
2329
|
-
} catch
|
|
2330
|
-
console.error("Failed to search memories:", error);
|
|
3056
|
+
} catch {
|
|
2331
3057
|
return [];
|
|
2332
3058
|
}
|
|
2333
3059
|
},
|
|
2334
|
-
[embeddingModel,
|
|
3060
|
+
[embeddingModel, embeddingOptions, storageCtx]
|
|
2335
3061
|
);
|
|
2336
|
-
const fetchAllMemories =
|
|
3062
|
+
const fetchAllMemories = useCallback3(async () => {
|
|
2337
3063
|
try {
|
|
2338
|
-
return await
|
|
3064
|
+
return await getAllMemoriesOp(storageCtx);
|
|
2339
3065
|
} catch (error) {
|
|
2340
3066
|
throw new Error(
|
|
2341
3067
|
"Failed to fetch all memories: " + (error instanceof Error ? error.message : String(error))
|
|
2342
3068
|
);
|
|
2343
3069
|
}
|
|
2344
|
-
}, []);
|
|
2345
|
-
const fetchMemoriesByNamespace =
|
|
3070
|
+
}, [storageCtx]);
|
|
3071
|
+
const fetchMemoriesByNamespace = useCallback3(
|
|
2346
3072
|
async (namespace) => {
|
|
2347
3073
|
if (!namespace) {
|
|
2348
3074
|
throw new Error("Missing required field: namespace");
|
|
2349
3075
|
}
|
|
2350
3076
|
try {
|
|
2351
|
-
return await
|
|
3077
|
+
return await getMemoriesByNamespaceOp(storageCtx, namespace);
|
|
2352
3078
|
} catch (error) {
|
|
2353
3079
|
throw new Error(
|
|
2354
3080
|
`Failed to fetch memories for namespace "${namespace}": ` + (error instanceof Error ? error.message : String(error))
|
|
2355
3081
|
);
|
|
2356
3082
|
}
|
|
2357
3083
|
},
|
|
2358
|
-
[]
|
|
3084
|
+
[storageCtx]
|
|
2359
3085
|
);
|
|
2360
|
-
const fetchMemoriesByKey =
|
|
3086
|
+
const fetchMemoriesByKey = useCallback3(
|
|
2361
3087
|
async (namespace, key) => {
|
|
2362
3088
|
if (!namespace || !key) {
|
|
2363
3089
|
throw new Error("Missing required fields: namespace, key");
|
|
2364
3090
|
}
|
|
2365
3091
|
try {
|
|
2366
|
-
return await
|
|
3092
|
+
return await getMemoriesByKeyOp(storageCtx, namespace, key);
|
|
2367
3093
|
} catch (error) {
|
|
2368
3094
|
throw new Error(
|
|
2369
3095
|
`Failed to fetch memories for "${namespace}:${key}": ` + (error instanceof Error ? error.message : String(error))
|
|
2370
3096
|
);
|
|
2371
3097
|
}
|
|
2372
3098
|
},
|
|
2373
|
-
[]
|
|
3099
|
+
[storageCtx]
|
|
2374
3100
|
);
|
|
2375
|
-
const
|
|
2376
|
-
async (id
|
|
2377
|
-
|
|
2378
|
-
|
|
3101
|
+
const getMemoryById = useCallback3(
|
|
3102
|
+
async (id) => {
|
|
3103
|
+
try {
|
|
3104
|
+
return await getMemoryByIdOp(storageCtx, id);
|
|
3105
|
+
} catch (error) {
|
|
3106
|
+
throw new Error(
|
|
3107
|
+
`Failed to get memory ${id}: ` + (error instanceof Error ? error.message : String(error))
|
|
3108
|
+
);
|
|
2379
3109
|
}
|
|
3110
|
+
},
|
|
3111
|
+
[storageCtx]
|
|
3112
|
+
);
|
|
3113
|
+
const saveMemory = useCallback3(
|
|
3114
|
+
async (memory) => {
|
|
2380
3115
|
try {
|
|
2381
|
-
const
|
|
2382
|
-
|
|
2383
|
-
model: embeddingModelToUse,
|
|
2384
|
-
provider: embeddingProvider,
|
|
2385
|
-
getToken: getToken || void 0,
|
|
2386
|
-
baseUrl
|
|
2387
|
-
};
|
|
2388
|
-
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) {
|
|
2389
|
-
throw new Error(
|
|
2390
|
-
"Missing required fields: type, namespace, key, value, rawEvidence, confidence, pii"
|
|
2391
|
-
);
|
|
2392
|
-
}
|
|
2393
|
-
const existingMemory = await getMemoryById(id);
|
|
2394
|
-
if (!existingMemory) {
|
|
2395
|
-
throw new Error(`Memory with id ${id} not found`);
|
|
2396
|
-
}
|
|
2397
|
-
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;
|
|
2398
|
-
if (!embeddingFieldsChanged) {
|
|
2399
|
-
return existingMemory;
|
|
2400
|
-
}
|
|
2401
|
-
const memory = {
|
|
2402
|
-
type: updates.type,
|
|
2403
|
-
namespace: updates.namespace,
|
|
2404
|
-
key: updates.key,
|
|
2405
|
-
value: updates.value,
|
|
2406
|
-
rawEvidence: updates.rawEvidence,
|
|
2407
|
-
confidence: updates.confidence,
|
|
2408
|
-
pii: updates.pii
|
|
2409
|
-
};
|
|
2410
|
-
let embedding = existingMemory.embedding ?? [];
|
|
2411
|
-
let embeddingModelToStore = existingMemory.embeddingModel ?? "";
|
|
2412
|
-
if (generateEmbeddings && embeddingModelToUse) {
|
|
3116
|
+
const saved = await saveMemoryOp(storageCtx, memory);
|
|
3117
|
+
if (generateEmbeddings && embeddingModel && !memory.embedding) {
|
|
2413
3118
|
try {
|
|
2414
|
-
|
|
2415
|
-
memory,
|
|
3119
|
+
const memoryItem = {
|
|
3120
|
+
type: memory.type,
|
|
3121
|
+
namespace: memory.namespace,
|
|
3122
|
+
key: memory.key,
|
|
3123
|
+
value: memory.value,
|
|
3124
|
+
rawEvidence: memory.rawEvidence,
|
|
3125
|
+
confidence: memory.confidence,
|
|
3126
|
+
pii: memory.pii
|
|
3127
|
+
};
|
|
3128
|
+
const embedding = await generateEmbeddingForMemory(
|
|
3129
|
+
memoryItem,
|
|
2416
3130
|
embeddingOptions
|
|
2417
3131
|
);
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
3132
|
+
await updateMemoryEmbeddingOp(
|
|
3133
|
+
storageCtx,
|
|
3134
|
+
saved.uniqueId,
|
|
3135
|
+
embedding,
|
|
3136
|
+
effectiveEmbeddingModel
|
|
2423
3137
|
);
|
|
3138
|
+
} catch (error) {
|
|
3139
|
+
console.error("Failed to generate embedding:", error);
|
|
2424
3140
|
}
|
|
2425
3141
|
}
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
3142
|
+
setMemories((prev) => {
|
|
3143
|
+
const existing = prev.find((m) => m.uniqueId === saved.uniqueId);
|
|
3144
|
+
if (existing) {
|
|
3145
|
+
return prev.map((m) => m.uniqueId === saved.uniqueId ? saved : m);
|
|
3146
|
+
}
|
|
3147
|
+
return [saved, ...prev];
|
|
3148
|
+
});
|
|
3149
|
+
return saved;
|
|
3150
|
+
} catch (error) {
|
|
3151
|
+
throw new Error(
|
|
3152
|
+
"Failed to save memory: " + (error instanceof Error ? error.message : String(error))
|
|
2432
3153
|
);
|
|
3154
|
+
}
|
|
3155
|
+
},
|
|
3156
|
+
[storageCtx, generateEmbeddings, embeddingModel, embeddingOptions]
|
|
3157
|
+
);
|
|
3158
|
+
const saveMemories = useCallback3(
|
|
3159
|
+
async (memoriesToSave) => {
|
|
3160
|
+
try {
|
|
3161
|
+
const saved = await saveMemoriesOp(storageCtx, memoriesToSave);
|
|
3162
|
+
if (generateEmbeddings && embeddingModel) {
|
|
3163
|
+
for (let i = 0; i < saved.length; i++) {
|
|
3164
|
+
const memory = memoriesToSave[i];
|
|
3165
|
+
if (!memory.embedding) {
|
|
3166
|
+
try {
|
|
3167
|
+
const memoryItem = {
|
|
3168
|
+
type: memory.type,
|
|
3169
|
+
namespace: memory.namespace,
|
|
3170
|
+
key: memory.key,
|
|
3171
|
+
value: memory.value,
|
|
3172
|
+
rawEvidence: memory.rawEvidence,
|
|
3173
|
+
confidence: memory.confidence,
|
|
3174
|
+
pii: memory.pii
|
|
3175
|
+
};
|
|
3176
|
+
const embedding = await generateEmbeddingForMemory(
|
|
3177
|
+
memoryItem,
|
|
3178
|
+
embeddingOptions
|
|
3179
|
+
);
|
|
3180
|
+
await updateMemoryEmbeddingOp(
|
|
3181
|
+
storageCtx,
|
|
3182
|
+
saved[i].uniqueId,
|
|
3183
|
+
embedding,
|
|
3184
|
+
effectiveEmbeddingModel
|
|
3185
|
+
);
|
|
3186
|
+
} catch (error) {
|
|
3187
|
+
console.error("Failed to generate embedding:", error);
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
}
|
|
3192
|
+
await refreshMemories();
|
|
3193
|
+
return saved;
|
|
2433
3194
|
} catch (error) {
|
|
2434
3195
|
throw new Error(
|
|
2435
|
-
|
|
3196
|
+
"Failed to save memories: " + (error instanceof Error ? error.message : String(error))
|
|
3197
|
+
);
|
|
3198
|
+
}
|
|
3199
|
+
},
|
|
3200
|
+
[
|
|
3201
|
+
storageCtx,
|
|
3202
|
+
generateEmbeddings,
|
|
3203
|
+
embeddingModel,
|
|
3204
|
+
embeddingOptions,
|
|
3205
|
+
refreshMemories
|
|
3206
|
+
]
|
|
3207
|
+
);
|
|
3208
|
+
const updateMemory = useCallback3(
|
|
3209
|
+
async (id, updates) => {
|
|
3210
|
+
const result = await updateMemoryOp(storageCtx, id, updates);
|
|
3211
|
+
if (!result.ok) {
|
|
3212
|
+
if (result.reason === "not_found") {
|
|
3213
|
+
return null;
|
|
3214
|
+
}
|
|
3215
|
+
if (result.reason === "conflict") {
|
|
3216
|
+
throw new Error(
|
|
3217
|
+
`Cannot update memory: a memory with key "${result.conflictingKey}" already exists`
|
|
3218
|
+
);
|
|
3219
|
+
}
|
|
3220
|
+
throw new Error(
|
|
3221
|
+
`Failed to update memory ${id}: ${result.error.message}`
|
|
2436
3222
|
);
|
|
2437
3223
|
}
|
|
3224
|
+
const updated = result.memory;
|
|
3225
|
+
const contentChanged = updates.value !== void 0 || updates.rawEvidence !== void 0 || updates.type !== void 0 || updates.namespace !== void 0 || updates.key !== void 0;
|
|
3226
|
+
if (contentChanged && generateEmbeddings && embeddingModel && !updates.embedding) {
|
|
3227
|
+
try {
|
|
3228
|
+
const memoryItem = {
|
|
3229
|
+
type: updated.type,
|
|
3230
|
+
namespace: updated.namespace,
|
|
3231
|
+
key: updated.key,
|
|
3232
|
+
value: updated.value,
|
|
3233
|
+
rawEvidence: updated.rawEvidence,
|
|
3234
|
+
confidence: updated.confidence,
|
|
3235
|
+
pii: updated.pii
|
|
3236
|
+
};
|
|
3237
|
+
const embedding = await generateEmbeddingForMemory(
|
|
3238
|
+
memoryItem,
|
|
3239
|
+
embeddingOptions
|
|
3240
|
+
);
|
|
3241
|
+
await updateMemoryEmbeddingOp(
|
|
3242
|
+
storageCtx,
|
|
3243
|
+
id,
|
|
3244
|
+
embedding,
|
|
3245
|
+
effectiveEmbeddingModel
|
|
3246
|
+
);
|
|
3247
|
+
} catch (error) {
|
|
3248
|
+
console.error("Failed to regenerate embedding:", error);
|
|
3249
|
+
}
|
|
3250
|
+
}
|
|
3251
|
+
setMemories(
|
|
3252
|
+
(prev) => prev.map((m) => m.uniqueId === id ? updated : m)
|
|
3253
|
+
);
|
|
3254
|
+
return updated;
|
|
2438
3255
|
},
|
|
2439
|
-
[
|
|
3256
|
+
[storageCtx, generateEmbeddings, embeddingModel, embeddingOptions]
|
|
2440
3257
|
);
|
|
2441
|
-
const removeMemory =
|
|
3258
|
+
const removeMemory = useCallback3(
|
|
2442
3259
|
async (namespace, key, value) => {
|
|
2443
3260
|
if (!namespace || !key || !value) {
|
|
2444
3261
|
throw new Error("Missing required fields: namespace, key, value");
|
|
2445
3262
|
}
|
|
2446
3263
|
try {
|
|
2447
|
-
await
|
|
3264
|
+
await deleteMemoryOp(storageCtx, namespace, key, value);
|
|
3265
|
+
setMemories(
|
|
3266
|
+
(prev) => prev.filter(
|
|
3267
|
+
(m) => !(m.namespace === namespace && m.key === key && m.value === value)
|
|
3268
|
+
)
|
|
3269
|
+
);
|
|
2448
3270
|
} catch (error) {
|
|
2449
3271
|
throw new Error(
|
|
2450
3272
|
`Failed to delete memory "${namespace}:${key}:${value}": ` + (error instanceof Error ? error.message : String(error))
|
|
2451
3273
|
);
|
|
2452
3274
|
}
|
|
2453
3275
|
},
|
|
2454
|
-
[]
|
|
3276
|
+
[storageCtx]
|
|
2455
3277
|
);
|
|
2456
|
-
const removeMemoryById =
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
}
|
|
2467
|
-
|
|
2468
|
-
|
|
3278
|
+
const removeMemoryById = useCallback3(
|
|
3279
|
+
async (id) => {
|
|
3280
|
+
try {
|
|
3281
|
+
await deleteMemoryByIdOp(storageCtx, id);
|
|
3282
|
+
setMemories((prev) => prev.filter((m) => m.uniqueId !== id));
|
|
3283
|
+
} catch (error) {
|
|
3284
|
+
throw new Error(
|
|
3285
|
+
`Failed to delete memory with id ${id}: ` + (error instanceof Error ? error.message : String(error))
|
|
3286
|
+
);
|
|
3287
|
+
}
|
|
3288
|
+
},
|
|
3289
|
+
[storageCtx]
|
|
3290
|
+
);
|
|
3291
|
+
const removeMemories = useCallback3(
|
|
2469
3292
|
async (namespace, key) => {
|
|
2470
3293
|
if (!namespace || !key) {
|
|
2471
3294
|
throw new Error("Missing required fields: namespace, key");
|
|
2472
3295
|
}
|
|
2473
3296
|
try {
|
|
2474
|
-
await
|
|
3297
|
+
await deleteMemoriesByKeyOp(storageCtx, namespace, key);
|
|
3298
|
+
setMemories(
|
|
3299
|
+
(prev) => prev.filter(
|
|
3300
|
+
(m) => !(m.namespace === namespace && m.key === key)
|
|
3301
|
+
)
|
|
3302
|
+
);
|
|
2475
3303
|
} catch (error) {
|
|
2476
3304
|
throw new Error(
|
|
2477
3305
|
`Failed to delete memories for "${namespace}:${key}": ` + (error instanceof Error ? error.message : String(error))
|
|
2478
3306
|
);
|
|
2479
3307
|
}
|
|
2480
3308
|
},
|
|
2481
|
-
[]
|
|
3309
|
+
[storageCtx]
|
|
2482
3310
|
);
|
|
2483
|
-
const clearMemories =
|
|
3311
|
+
const clearMemories = useCallback3(async () => {
|
|
2484
3312
|
try {
|
|
2485
|
-
await
|
|
3313
|
+
await clearAllMemoriesOp(storageCtx);
|
|
3314
|
+
setMemories([]);
|
|
2486
3315
|
} catch (error) {
|
|
2487
3316
|
throw new Error(
|
|
2488
3317
|
"Failed to clear all memories: " + (error instanceof Error ? error.message : String(error))
|
|
2489
3318
|
);
|
|
2490
3319
|
}
|
|
2491
|
-
}, []);
|
|
3320
|
+
}, [storageCtx]);
|
|
2492
3321
|
return {
|
|
3322
|
+
memories,
|
|
3323
|
+
refreshMemories,
|
|
2493
3324
|
extractMemoriesFromMessage,
|
|
2494
3325
|
searchMemories,
|
|
2495
3326
|
fetchAllMemories,
|
|
2496
3327
|
fetchMemoriesByNamespace,
|
|
2497
3328
|
fetchMemoriesByKey,
|
|
3329
|
+
getMemoryById,
|
|
3330
|
+
saveMemory,
|
|
3331
|
+
saveMemories,
|
|
2498
3332
|
updateMemory,
|
|
2499
3333
|
removeMemory,
|
|
2500
3334
|
removeMemoryById,
|
|
@@ -2503,8 +3337,115 @@ function useMemory(options = {}) {
|
|
|
2503
3337
|
};
|
|
2504
3338
|
}
|
|
2505
3339
|
|
|
3340
|
+
// src/lib/memoryStorage/schema.ts
|
|
3341
|
+
import { appSchema as appSchema2, tableSchema as tableSchema2 } from "@nozbe/watermelondb";
|
|
3342
|
+
var memoryStorageSchema = appSchema2({
|
|
3343
|
+
version: 1,
|
|
3344
|
+
tables: [
|
|
3345
|
+
tableSchema2({
|
|
3346
|
+
name: "memories",
|
|
3347
|
+
columns: [
|
|
3348
|
+
// Memory type classification
|
|
3349
|
+
{ name: "type", type: "string", isIndexed: true },
|
|
3350
|
+
// 'identity' | 'preference' | 'project' | 'skill' | 'constraint'
|
|
3351
|
+
// Hierarchical key structure
|
|
3352
|
+
{ name: "namespace", type: "string", isIndexed: true },
|
|
3353
|
+
{ name: "key", type: "string", isIndexed: true },
|
|
3354
|
+
{ name: "value", type: "string" },
|
|
3355
|
+
// Evidence and confidence
|
|
3356
|
+
{ name: "raw_evidence", type: "string" },
|
|
3357
|
+
{ name: "confidence", type: "number" },
|
|
3358
|
+
{ name: "pii", type: "boolean", isIndexed: true },
|
|
3359
|
+
// Composite keys for efficient lookups
|
|
3360
|
+
{ name: "composite_key", type: "string", isIndexed: true },
|
|
3361
|
+
// namespace:key
|
|
3362
|
+
{ name: "unique_key", type: "string", isIndexed: true },
|
|
3363
|
+
// namespace:key:value
|
|
3364
|
+
// Timestamps
|
|
3365
|
+
{ name: "created_at", type: "number", isIndexed: true },
|
|
3366
|
+
{ name: "updated_at", type: "number" },
|
|
3367
|
+
// Vector embeddings for semantic search
|
|
3368
|
+
{ name: "embedding", type: "string", isOptional: true },
|
|
3369
|
+
// JSON stringified number[]
|
|
3370
|
+
{ name: "embedding_model", type: "string", isOptional: true },
|
|
3371
|
+
// Soft delete flag
|
|
3372
|
+
{ name: "is_deleted", type: "boolean", isIndexed: true }
|
|
3373
|
+
]
|
|
3374
|
+
})
|
|
3375
|
+
]
|
|
3376
|
+
});
|
|
3377
|
+
|
|
3378
|
+
// src/lib/memoryStorage/models.ts
|
|
3379
|
+
import { Model as Model2 } from "@nozbe/watermelondb";
|
|
3380
|
+
var Memory = class extends Model2 {
|
|
3381
|
+
/** Memory type classification */
|
|
3382
|
+
get type() {
|
|
3383
|
+
return this._getRaw("type");
|
|
3384
|
+
}
|
|
3385
|
+
/** Namespace for grouping related memories */
|
|
3386
|
+
get namespace() {
|
|
3387
|
+
return this._getRaw("namespace");
|
|
3388
|
+
}
|
|
3389
|
+
/** Key within the namespace */
|
|
3390
|
+
get key() {
|
|
3391
|
+
return this._getRaw("key");
|
|
3392
|
+
}
|
|
3393
|
+
/** The memory value/content */
|
|
3394
|
+
get value() {
|
|
3395
|
+
return this._getRaw("value");
|
|
3396
|
+
}
|
|
3397
|
+
/** Raw evidence from which this memory was extracted */
|
|
3398
|
+
get rawEvidence() {
|
|
3399
|
+
return this._getRaw("raw_evidence");
|
|
3400
|
+
}
|
|
3401
|
+
/** Confidence score (0-1) */
|
|
3402
|
+
get confidence() {
|
|
3403
|
+
return this._getRaw("confidence");
|
|
3404
|
+
}
|
|
3405
|
+
/** Whether this memory contains PII */
|
|
3406
|
+
get pii() {
|
|
3407
|
+
return this._getRaw("pii");
|
|
3408
|
+
}
|
|
3409
|
+
/** Composite key (namespace:key) for efficient lookups */
|
|
3410
|
+
get compositeKey() {
|
|
3411
|
+
return this._getRaw("composite_key");
|
|
3412
|
+
}
|
|
3413
|
+
/** Unique key (namespace:key:value) for deduplication */
|
|
3414
|
+
get uniqueKey() {
|
|
3415
|
+
return this._getRaw("unique_key");
|
|
3416
|
+
}
|
|
3417
|
+
/** Created timestamp */
|
|
3418
|
+
get createdAt() {
|
|
3419
|
+
return new Date(this._getRaw("created_at"));
|
|
3420
|
+
}
|
|
3421
|
+
/** Updated timestamp */
|
|
3422
|
+
get updatedAt() {
|
|
3423
|
+
return new Date(this._getRaw("updated_at"));
|
|
3424
|
+
}
|
|
3425
|
+
/** Embedding vector for semantic search */
|
|
3426
|
+
get embedding() {
|
|
3427
|
+
const raw = this._getRaw("embedding");
|
|
3428
|
+
if (!raw) return void 0;
|
|
3429
|
+
try {
|
|
3430
|
+
return JSON.parse(raw);
|
|
3431
|
+
} catch {
|
|
3432
|
+
return void 0;
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
/** Model used to generate embedding */
|
|
3436
|
+
get embeddingModel() {
|
|
3437
|
+
const value = this._getRaw("embedding_model");
|
|
3438
|
+
return value ? value : void 0;
|
|
3439
|
+
}
|
|
3440
|
+
/** Soft delete flag */
|
|
3441
|
+
get isDeleted() {
|
|
3442
|
+
return this._getRaw("is_deleted");
|
|
3443
|
+
}
|
|
3444
|
+
};
|
|
3445
|
+
Memory.table = "memories";
|
|
3446
|
+
|
|
2506
3447
|
// src/react/usePdf.ts
|
|
2507
|
-
import { useCallback as
|
|
3448
|
+
import { useCallback as useCallback4, useState as useState4 } from "react";
|
|
2508
3449
|
|
|
2509
3450
|
// src/lib/pdf.ts
|
|
2510
3451
|
import * as pdfjs from "pdfjs-dist";
|
|
@@ -2557,9 +3498,9 @@ async function convertPdfToImages(pdfDataUrl) {
|
|
|
2557
3498
|
// src/react/usePdf.ts
|
|
2558
3499
|
var PDF_MIME_TYPE = "application/pdf";
|
|
2559
3500
|
function usePdf() {
|
|
2560
|
-
const [isProcessing, setIsProcessing] =
|
|
2561
|
-
const [error, setError] =
|
|
2562
|
-
const extractPdfContext =
|
|
3501
|
+
const [isProcessing, setIsProcessing] = useState4(false);
|
|
3502
|
+
const [error, setError] = useState4(null);
|
|
3503
|
+
const extractPdfContext = useCallback4(
|
|
2563
3504
|
async (files) => {
|
|
2564
3505
|
setIsProcessing(true);
|
|
2565
3506
|
setError(null);
|
|
@@ -2606,12 +3547,12 @@ ${text}`;
|
|
|
2606
3547
|
}
|
|
2607
3548
|
|
|
2608
3549
|
// src/react/useOCR.ts
|
|
2609
|
-
import { useCallback as
|
|
3550
|
+
import { useCallback as useCallback5, useState as useState5 } from "react";
|
|
2610
3551
|
import Tesseract from "tesseract.js";
|
|
2611
3552
|
function useOCR() {
|
|
2612
|
-
const [isProcessing, setIsProcessing] =
|
|
2613
|
-
const [error, setError] =
|
|
2614
|
-
const extractOCRContext =
|
|
3553
|
+
const [isProcessing, setIsProcessing] = useState5(false);
|
|
3554
|
+
const [error, setError] = useState5(null);
|
|
3555
|
+
const extractOCRContext = useCallback5(
|
|
2615
3556
|
async (files) => {
|
|
2616
3557
|
setIsProcessing(true);
|
|
2617
3558
|
setError(null);
|
|
@@ -2697,12 +3638,12 @@ ${text}`;
|
|
|
2697
3638
|
}
|
|
2698
3639
|
|
|
2699
3640
|
// src/react/useModels.ts
|
|
2700
|
-
import { useCallback as
|
|
3641
|
+
import { useCallback as useCallback6, useEffect as useEffect2, useRef as useRef3, useState as useState6 } from "react";
|
|
2701
3642
|
function useModels(options = {}) {
|
|
2702
3643
|
const { getToken, baseUrl = BASE_URL, provider, autoFetch = true } = options;
|
|
2703
|
-
const [models, setModels] =
|
|
2704
|
-
const [isLoading, setIsLoading] =
|
|
2705
|
-
const [error, setError] =
|
|
3644
|
+
const [models, setModels] = useState6([]);
|
|
3645
|
+
const [isLoading, setIsLoading] = useState6(false);
|
|
3646
|
+
const [error, setError] = useState6(null);
|
|
2706
3647
|
const getTokenRef = useRef3(getToken);
|
|
2707
3648
|
const baseUrlRef = useRef3(baseUrl);
|
|
2708
3649
|
const providerRef = useRef3(provider);
|
|
@@ -2720,7 +3661,7 @@ function useModels(options = {}) {
|
|
|
2720
3661
|
}
|
|
2721
3662
|
};
|
|
2722
3663
|
}, []);
|
|
2723
|
-
const fetchModels =
|
|
3664
|
+
const fetchModels = useCallback6(async () => {
|
|
2724
3665
|
if (abortControllerRef.current) {
|
|
2725
3666
|
abortControllerRef.current.abort();
|
|
2726
3667
|
}
|
|
@@ -2778,7 +3719,7 @@ function useModels(options = {}) {
|
|
|
2778
3719
|
}
|
|
2779
3720
|
}
|
|
2780
3721
|
}, []);
|
|
2781
|
-
const refetch =
|
|
3722
|
+
const refetch = useCallback6(async () => {
|
|
2782
3723
|
setModels([]);
|
|
2783
3724
|
await fetchModels();
|
|
2784
3725
|
}, [fetchModels]);
|
|
@@ -2801,13 +3742,13 @@ function useModels(options = {}) {
|
|
|
2801
3742
|
}
|
|
2802
3743
|
|
|
2803
3744
|
// src/react/useSearch.ts
|
|
2804
|
-
import { useCallback as
|
|
3745
|
+
import { useCallback as useCallback7, useEffect as useEffect3, useRef as useRef4, useState as useState7 } from "react";
|
|
2805
3746
|
function useSearch(options = {}) {
|
|
2806
3747
|
const { getToken, baseUrl = BASE_URL, onError } = options;
|
|
2807
|
-
const [isLoading, setIsLoading] =
|
|
2808
|
-
const [results, setResults] =
|
|
2809
|
-
const [response, setResponse] =
|
|
2810
|
-
const [error, setError] =
|
|
3748
|
+
const [isLoading, setIsLoading] = useState7(false);
|
|
3749
|
+
const [results, setResults] = useState7(null);
|
|
3750
|
+
const [response, setResponse] = useState7(null);
|
|
3751
|
+
const [error, setError] = useState7(null);
|
|
2811
3752
|
const abortControllerRef = useRef4(null);
|
|
2812
3753
|
useEffect3(() => {
|
|
2813
3754
|
return () => {
|
|
@@ -2817,7 +3758,7 @@ function useSearch(options = {}) {
|
|
|
2817
3758
|
}
|
|
2818
3759
|
};
|
|
2819
3760
|
}, []);
|
|
2820
|
-
const search =
|
|
3761
|
+
const search = useCallback7(
|
|
2821
3762
|
async (query, searchOptions = {}) => {
|
|
2822
3763
|
if (abortControllerRef.current) {
|
|
2823
3764
|
abortControllerRef.current.abort();
|
|
@@ -2885,10 +3826,10 @@ function useSearch(options = {}) {
|
|
|
2885
3826
|
}
|
|
2886
3827
|
|
|
2887
3828
|
// src/react/useImageGeneration.ts
|
|
2888
|
-
import { useCallback as
|
|
3829
|
+
import { useCallback as useCallback8, useEffect as useEffect4, useRef as useRef5, useState as useState8 } from "react";
|
|
2889
3830
|
function useImageGeneration(options = {}) {
|
|
2890
3831
|
const { getToken, baseUrl = BASE_URL, onFinish, onError } = options;
|
|
2891
|
-
const [isLoading, setIsLoading] =
|
|
3832
|
+
const [isLoading, setIsLoading] = useState8(false);
|
|
2892
3833
|
const abortControllerRef = useRef5(null);
|
|
2893
3834
|
useEffect4(() => {
|
|
2894
3835
|
return () => {
|
|
@@ -2898,13 +3839,13 @@ function useImageGeneration(options = {}) {
|
|
|
2898
3839
|
}
|
|
2899
3840
|
};
|
|
2900
3841
|
}, []);
|
|
2901
|
-
const stop =
|
|
3842
|
+
const stop = useCallback8(() => {
|
|
2902
3843
|
if (abortControllerRef.current) {
|
|
2903
3844
|
abortControllerRef.current.abort();
|
|
2904
3845
|
abortControllerRef.current = null;
|
|
2905
3846
|
}
|
|
2906
3847
|
}, []);
|
|
2907
|
-
const generateImage =
|
|
3848
|
+
const generateImage = useCallback8(
|
|
2908
3849
|
async (args) => {
|
|
2909
3850
|
if (abortControllerRef.current) {
|
|
2910
3851
|
abortControllerRef.current.abort();
|
|
@@ -3021,7 +3962,11 @@ var extractConversationContext = (messages, maxMessages = 3) => {
|
|
|
3021
3962
|
return userMessages.trim();
|
|
3022
3963
|
};
|
|
3023
3964
|
export {
|
|
3965
|
+
Conversation as ChatConversation,
|
|
3966
|
+
Message as ChatMessage,
|
|
3024
3967
|
DEFAULT_TOOL_SELECTOR_MODEL,
|
|
3968
|
+
Memory as StoredMemoryModel,
|
|
3969
|
+
chatStorageSchema,
|
|
3025
3970
|
createMemoryContextSystemMessage,
|
|
3026
3971
|
decryptData,
|
|
3027
3972
|
decryptDataBytes,
|
|
@@ -3029,13 +3974,18 @@ export {
|
|
|
3029
3974
|
executeTool,
|
|
3030
3975
|
extractConversationContext,
|
|
3031
3976
|
formatMemoriesForChat,
|
|
3977
|
+
generateCompositeKey,
|
|
3978
|
+
generateConversationId,
|
|
3979
|
+
generateUniqueKey,
|
|
3032
3980
|
hasEncryptionKey,
|
|
3981
|
+
memoryStorageSchema,
|
|
3033
3982
|
requestEncryptionKey,
|
|
3034
3983
|
selectTool,
|
|
3035
3984
|
useChat,
|
|
3985
|
+
useChatStorage,
|
|
3036
3986
|
useEncryption,
|
|
3037
3987
|
useImageGeneration,
|
|
3038
|
-
|
|
3988
|
+
useMemoryStorage,
|
|
3039
3989
|
useModels,
|
|
3040
3990
|
useOCR,
|
|
3041
3991
|
usePdf,
|