@reverbia/sdk 1.0.0 → 1.1.0-next.20251230221037
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +290 -45
- package/dist/expo/index.cjs +3428 -0
- package/dist/expo/index.d.mts +1217 -0
- package/dist/expo/index.d.ts +1217 -0
- package/dist/expo/index.mjs +3395 -0
- package/dist/index.cjs +972 -0
- package/dist/index.d.mts +1514 -0
- package/dist/index.d.ts +1514 -0
- package/dist/index.mjs +934 -0
- package/dist/next/index.cjs +64 -0
- package/dist/next/index.d.mts +23 -0
- package/dist/next/index.d.ts +23 -0
- package/dist/next/index.mjs +39 -0
- package/dist/polyfills/index.cjs +61 -0
- package/dist/polyfills/index.d.mts +9 -0
- package/dist/polyfills/index.d.ts +9 -0
- package/dist/polyfills/index.mjs +34 -0
- package/dist/react/chunk-KUFGQF6E.mjs +290 -0
- package/dist/react/chunk-T56Y62G7.mjs +410 -0
- package/dist/react/index.cjs +7982 -0
- package/dist/react/index.d.mts +3139 -0
- package/dist/react/index.d.ts +3139 -0
- package/dist/react/index.mjs +7209 -0
- package/dist/react/storage-Z2NBANCK.mjs +29 -0
- package/dist/react/useEncryption-5RTXKDNZ.mjs +31 -0
- package/dist/vercel/index.cjs +86 -0
- package/dist/vercel/index.d.mts +119 -0
- package/dist/vercel/index.d.ts +119 -0
- package/dist/vercel/index.mjs +57 -0
- package/package.json +91 -16
- package/dist/cjs/client/client/client.gen.d.ts +0 -2
- package/dist/cjs/client/client/client.gen.js +0 -203
- package/dist/cjs/client/client/index.d.ts +0 -8
- package/dist/cjs/client/client/index.js +0 -16
- package/dist/cjs/client/client/types.gen.d.ts +0 -99
- package/dist/cjs/client/client/types.gen.js +0 -3
- package/dist/cjs/client/client/utils.gen.d.ts +0 -40
- package/dist/cjs/client/client/utils.gen.js +0 -314
- package/dist/cjs/client/client.gen.d.ts +0 -12
- package/dist/cjs/client/client.gen.js +0 -6
- package/dist/cjs/client/core/auth.gen.d.ts +0 -18
- package/dist/cjs/client/core/auth.gen.js +0 -18
- package/dist/cjs/client/core/bodySerializer.gen.d.ts +0 -25
- package/dist/cjs/client/core/bodySerializer.gen.js +0 -60
- package/dist/cjs/client/core/params.gen.d.ts +0 -43
- package/dist/cjs/client/core/params.gen.js +0 -104
- package/dist/cjs/client/core/pathSerializer.gen.d.ts +0 -33
- package/dist/cjs/client/core/pathSerializer.gen.js +0 -123
- package/dist/cjs/client/core/queryKeySerializer.gen.d.ts +0 -18
- package/dist/cjs/client/core/queryKeySerializer.gen.js +0 -105
- package/dist/cjs/client/core/serverSentEvents.gen.d.ts +0 -71
- package/dist/cjs/client/core/serverSentEvents.gen.js +0 -139
- package/dist/cjs/client/core/types.gen.d.ts +0 -78
- package/dist/cjs/client/core/types.gen.js +0 -3
- package/dist/cjs/client/core/utils.gen.d.ts +0 -19
- package/dist/cjs/client/core/utils.gen.js +0 -93
- package/dist/cjs/client/index.d.ts +0 -2
- package/dist/cjs/client/index.js +0 -18
- package/dist/cjs/client/sdk.gen.d.ts +0 -27
- package/dist/cjs/client/sdk.gen.js +0 -33
- package/dist/cjs/client/types.gen.d.ts +0 -120
- package/dist/cjs/client/types.gen.js +0 -3
- package/dist/esm/client/client/client.gen.d.ts +0 -2
- package/dist/esm/client/client/client.gen.js +0 -199
- package/dist/esm/client/client/index.d.ts +0 -8
- package/dist/esm/client/client/index.js +0 -6
- package/dist/esm/client/client/types.gen.d.ts +0 -99
- package/dist/esm/client/client/types.gen.js +0 -2
- package/dist/esm/client/client/utils.gen.d.ts +0 -40
- package/dist/esm/client/client/utils.gen.js +0 -302
- package/dist/esm/client/client.gen.d.ts +0 -12
- package/dist/esm/client/client.gen.js +0 -3
- package/dist/esm/client/core/auth.gen.d.ts +0 -18
- package/dist/esm/client/core/auth.gen.js +0 -14
- package/dist/esm/client/core/bodySerializer.gen.d.ts +0 -25
- package/dist/esm/client/core/bodySerializer.gen.js +0 -57
- package/dist/esm/client/core/params.gen.d.ts +0 -43
- package/dist/esm/client/core/params.gen.js +0 -100
- package/dist/esm/client/core/pathSerializer.gen.d.ts +0 -33
- package/dist/esm/client/core/pathSerializer.gen.js +0 -114
- package/dist/esm/client/core/queryKeySerializer.gen.d.ts +0 -18
- package/dist/esm/client/core/queryKeySerializer.gen.js +0 -99
- package/dist/esm/client/core/serverSentEvents.gen.d.ts +0 -71
- package/dist/esm/client/core/serverSentEvents.gen.js +0 -135
- package/dist/esm/client/core/types.gen.d.ts +0 -78
- package/dist/esm/client/core/types.gen.js +0 -2
- package/dist/esm/client/core/utils.gen.d.ts +0 -19
- package/dist/esm/client/core/utils.gen.js +0 -87
- package/dist/esm/client/index.d.ts +0 -2
- package/dist/esm/client/index.js +0 -2
- package/dist/esm/client/sdk.gen.d.ts +0 -27
- package/dist/esm/client/sdk.gen.js +0 -28
- package/dist/esm/client/types.gen.d.ts +0 -120
- package/dist/esm/client/types.gen.js +0 -2
- package/dist/types/client/client/client.gen.d.ts +0 -2
- package/dist/types/client/client/index.d.ts +0 -8
- package/dist/types/client/client/types.gen.d.ts +0 -99
- package/dist/types/client/client/utils.gen.d.ts +0 -40
- package/dist/types/client/client.gen.d.ts +0 -12
- package/dist/types/client/core/auth.gen.d.ts +0 -18
- package/dist/types/client/core/bodySerializer.gen.d.ts +0 -25
- package/dist/types/client/core/params.gen.d.ts +0 -43
- package/dist/types/client/core/pathSerializer.gen.d.ts +0 -33
- package/dist/types/client/core/queryKeySerializer.gen.d.ts +0 -18
- package/dist/types/client/core/serverSentEvents.gen.d.ts +0 -71
- package/dist/types/client/core/types.gen.d.ts +0 -78
- package/dist/types/client/core/utils.gen.d.ts +0 -19
- package/dist/types/client/index.d.ts +0 -2
- package/dist/types/client/sdk.gen.d.ts +0 -27
- package/dist/types/client/types.gen.d.ts +0 -120
|
@@ -0,0 +1,3395 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result) __defProp(target, key, result);
|
|
9
|
+
return result;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/expo/useChat.ts
|
|
13
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
14
|
+
|
|
15
|
+
// src/clientConfig.ts
|
|
16
|
+
var BASE_URL = "https://ai-portal-dev.zetachain.com";
|
|
17
|
+
var createClientConfig = (config) => ({
|
|
18
|
+
...config,
|
|
19
|
+
baseUrl: BASE_URL
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// src/lib/chat/useChat/utils.ts
|
|
23
|
+
var VALIDATION_ERROR_MESSAGES = {
|
|
24
|
+
messages_required: "messages are required to call sendMessage.",
|
|
25
|
+
model_required: "model is required to call sendMessage.",
|
|
26
|
+
token_getter_required: "Token getter function is required.",
|
|
27
|
+
token_unavailable: "No access token available."
|
|
28
|
+
};
|
|
29
|
+
function validateMessages(messages) {
|
|
30
|
+
if (!messages?.length) {
|
|
31
|
+
return {
|
|
32
|
+
valid: false,
|
|
33
|
+
error: "messages_required",
|
|
34
|
+
message: VALIDATION_ERROR_MESSAGES.messages_required
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return { valid: true };
|
|
38
|
+
}
|
|
39
|
+
function validateModel(model) {
|
|
40
|
+
if (!model) {
|
|
41
|
+
return {
|
|
42
|
+
valid: false,
|
|
43
|
+
error: "model_required",
|
|
44
|
+
message: VALIDATION_ERROR_MESSAGES.model_required
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return { valid: true };
|
|
48
|
+
}
|
|
49
|
+
function validateTokenGetter(getToken) {
|
|
50
|
+
if (!getToken) {
|
|
51
|
+
return {
|
|
52
|
+
valid: false,
|
|
53
|
+
error: "token_getter_required",
|
|
54
|
+
message: VALIDATION_ERROR_MESSAGES.token_getter_required
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return { valid: true };
|
|
58
|
+
}
|
|
59
|
+
function validateToken(token) {
|
|
60
|
+
if (!token) {
|
|
61
|
+
return {
|
|
62
|
+
valid: false,
|
|
63
|
+
error: "token_unavailable",
|
|
64
|
+
message: VALIDATION_ERROR_MESSAGES.token_unavailable
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return { valid: true };
|
|
68
|
+
}
|
|
69
|
+
function extractTextFromContentPart(part) {
|
|
70
|
+
if (part.type === "text" && part.text) {
|
|
71
|
+
return part.text;
|
|
72
|
+
}
|
|
73
|
+
return "";
|
|
74
|
+
}
|
|
75
|
+
function messagesToInput(messages) {
|
|
76
|
+
return messages.map((msg) => {
|
|
77
|
+
const role = msg.role || "user";
|
|
78
|
+
const content = Array.isArray(msg.content) ? msg.content.map(extractTextFromContentPart).filter(Boolean).join("\n") : String(msg.content || "");
|
|
79
|
+
return `${role}: ${content}`;
|
|
80
|
+
}).join("\n\n");
|
|
81
|
+
}
|
|
82
|
+
function createStreamAccumulator() {
|
|
83
|
+
return {
|
|
84
|
+
content: "",
|
|
85
|
+
thinking: "",
|
|
86
|
+
responseId: "",
|
|
87
|
+
responseModel: "",
|
|
88
|
+
usage: {}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function buildResponseResponse(accumulator) {
|
|
92
|
+
const output = [];
|
|
93
|
+
if (accumulator.thinking) {
|
|
94
|
+
output.push({
|
|
95
|
+
type: "reasoning",
|
|
96
|
+
role: "assistant",
|
|
97
|
+
content: [{ type: "output_text", text: accumulator.thinking }],
|
|
98
|
+
status: "completed"
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
output.push({
|
|
102
|
+
type: "message",
|
|
103
|
+
role: "assistant",
|
|
104
|
+
content: [{ type: "output_text", text: accumulator.content }],
|
|
105
|
+
status: "completed"
|
|
106
|
+
});
|
|
107
|
+
return {
|
|
108
|
+
id: accumulator.responseId,
|
|
109
|
+
model: accumulator.responseModel,
|
|
110
|
+
object: "response",
|
|
111
|
+
output,
|
|
112
|
+
usage: Object.keys(accumulator.usage).length > 0 ? accumulator.usage : void 0
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function createErrorResult(message, onError) {
|
|
116
|
+
if (onError) {
|
|
117
|
+
onError(new Error(message));
|
|
118
|
+
}
|
|
119
|
+
return { data: null, error: message };
|
|
120
|
+
}
|
|
121
|
+
function handleError(err, onError) {
|
|
122
|
+
const errorMsg = err instanceof Error ? err.message : "Failed to send message.";
|
|
123
|
+
const errorObj = err instanceof Error ? err : new Error(errorMsg);
|
|
124
|
+
if (onError) {
|
|
125
|
+
onError(errorObj);
|
|
126
|
+
}
|
|
127
|
+
return { data: null, error: errorMsg };
|
|
128
|
+
}
|
|
129
|
+
function parseSSEDataLine(line) {
|
|
130
|
+
if (!line.startsWith("data: ")) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
const data = line.substring(6).trim();
|
|
134
|
+
if (data === "[DONE]") {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
return JSON.parse(data);
|
|
139
|
+
} catch {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// src/expo/useChat.ts
|
|
145
|
+
function processSSELines(lines, accumulator, onData, globalOnData, onThinking, globalOnThinking) {
|
|
146
|
+
for (const line of lines) {
|
|
147
|
+
const chunk = parseSSEDataLine(line);
|
|
148
|
+
if (!chunk) continue;
|
|
149
|
+
if (chunk.type === "response.created" && chunk.response && typeof chunk.response === "object") {
|
|
150
|
+
const response = chunk.response;
|
|
151
|
+
if (response.id && !accumulator.responseId) {
|
|
152
|
+
accumulator.responseId = response.id;
|
|
153
|
+
}
|
|
154
|
+
if (response.model && !accumulator.responseModel) {
|
|
155
|
+
accumulator.responseModel = response.model;
|
|
156
|
+
}
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (chunk.type === "response.completed" && chunk.response && typeof chunk.response === "object") {
|
|
160
|
+
const response = chunk.response;
|
|
161
|
+
if (response.usage) {
|
|
162
|
+
accumulator.usage = {
|
|
163
|
+
...accumulator.usage,
|
|
164
|
+
prompt_tokens: response.usage.input_tokens,
|
|
165
|
+
completion_tokens: response.usage.output_tokens,
|
|
166
|
+
total_tokens: (response.usage.input_tokens || 0) + (response.usage.output_tokens || 0)
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (chunk.id && !accumulator.responseId) {
|
|
172
|
+
accumulator.responseId = chunk.id;
|
|
173
|
+
}
|
|
174
|
+
if (chunk.model && !accumulator.responseModel) {
|
|
175
|
+
accumulator.responseModel = chunk.model;
|
|
176
|
+
}
|
|
177
|
+
if (chunk.usage) {
|
|
178
|
+
accumulator.usage = { ...accumulator.usage, ...chunk.usage };
|
|
179
|
+
}
|
|
180
|
+
if (chunk.type === "response.reasoning.delta" || chunk.type === "response.reasoning_summary_text.delta" || chunk.type === "response.thinking.delta") {
|
|
181
|
+
const delta = chunk.delta;
|
|
182
|
+
if (delta) {
|
|
183
|
+
const deltaText = typeof delta === "string" ? delta : delta.OfString || delta.OfResponseReasoningSummaryDeltaEventDelta;
|
|
184
|
+
if (deltaText) {
|
|
185
|
+
accumulator.thinking += deltaText;
|
|
186
|
+
if (onThinking) onThinking(deltaText);
|
|
187
|
+
if (globalOnThinking) globalOnThinking(deltaText);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (chunk.type === "response.output_text.delta" && chunk.delta) {
|
|
193
|
+
const deltaText = typeof chunk.delta === "string" ? chunk.delta : chunk.delta.OfString;
|
|
194
|
+
if (deltaText) {
|
|
195
|
+
accumulator.content += deltaText;
|
|
196
|
+
if (onData) onData(deltaText);
|
|
197
|
+
if (globalOnData) globalOnData(deltaText);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function useChat(options) {
|
|
203
|
+
const {
|
|
204
|
+
getToken,
|
|
205
|
+
baseUrl = BASE_URL,
|
|
206
|
+
onData: globalOnData,
|
|
207
|
+
onThinking: globalOnThinking,
|
|
208
|
+
onFinish,
|
|
209
|
+
onError
|
|
210
|
+
} = options || {};
|
|
211
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
212
|
+
const abortControllerRef = useRef(null);
|
|
213
|
+
const stop = useCallback(() => {
|
|
214
|
+
if (abortControllerRef.current) {
|
|
215
|
+
abortControllerRef.current.abort();
|
|
216
|
+
abortControllerRef.current = null;
|
|
217
|
+
}
|
|
218
|
+
}, []);
|
|
219
|
+
useEffect(() => {
|
|
220
|
+
return () => {
|
|
221
|
+
if (abortControllerRef.current) {
|
|
222
|
+
abortControllerRef.current.abort();
|
|
223
|
+
abortControllerRef.current = null;
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}, []);
|
|
227
|
+
const sendMessage = useCallback(
|
|
228
|
+
async ({
|
|
229
|
+
messages,
|
|
230
|
+
model,
|
|
231
|
+
onData,
|
|
232
|
+
onThinking,
|
|
233
|
+
// Responses API options
|
|
234
|
+
store,
|
|
235
|
+
previousResponseId,
|
|
236
|
+
conversation,
|
|
237
|
+
temperature,
|
|
238
|
+
maxOutputTokens,
|
|
239
|
+
tools,
|
|
240
|
+
toolChoice,
|
|
241
|
+
reasoning,
|
|
242
|
+
thinking
|
|
243
|
+
}) => {
|
|
244
|
+
const messagesValidation = validateMessages(messages);
|
|
245
|
+
if (!messagesValidation.valid) {
|
|
246
|
+
return createErrorResult(messagesValidation.message, onError);
|
|
247
|
+
}
|
|
248
|
+
const modelValidation = validateModel(model);
|
|
249
|
+
if (!modelValidation.valid) {
|
|
250
|
+
return createErrorResult(modelValidation.message, onError);
|
|
251
|
+
}
|
|
252
|
+
const tokenGetterValidation = validateTokenGetter(getToken);
|
|
253
|
+
if (!tokenGetterValidation.valid) {
|
|
254
|
+
return createErrorResult(tokenGetterValidation.message, onError);
|
|
255
|
+
}
|
|
256
|
+
if (abortControllerRef.current) {
|
|
257
|
+
abortControllerRef.current.abort();
|
|
258
|
+
}
|
|
259
|
+
const abortController = new AbortController();
|
|
260
|
+
abortControllerRef.current = abortController;
|
|
261
|
+
setIsLoading(true);
|
|
262
|
+
try {
|
|
263
|
+
const token = await getToken();
|
|
264
|
+
const tokenValidation = validateToken(token);
|
|
265
|
+
if (!tokenValidation.valid) {
|
|
266
|
+
setIsLoading(false);
|
|
267
|
+
return createErrorResult(tokenValidation.message, onError);
|
|
268
|
+
}
|
|
269
|
+
const result = await new Promise((resolve) => {
|
|
270
|
+
const xhr = new XMLHttpRequest();
|
|
271
|
+
const url = `${baseUrl}/api/v1/responses`;
|
|
272
|
+
const accumulator = createStreamAccumulator();
|
|
273
|
+
let lastProcessedIndex = 0;
|
|
274
|
+
let incompleteLineBuffer = "";
|
|
275
|
+
const abortHandler = () => {
|
|
276
|
+
xhr.abort();
|
|
277
|
+
};
|
|
278
|
+
abortController.signal.addEventListener("abort", abortHandler);
|
|
279
|
+
xhr.open("POST", url, true);
|
|
280
|
+
xhr.setRequestHeader("Content-Type", "application/json");
|
|
281
|
+
xhr.setRequestHeader("Authorization", `Bearer ${token}`);
|
|
282
|
+
xhr.setRequestHeader("Accept", "text/event-stream");
|
|
283
|
+
xhr.onprogress = () => {
|
|
284
|
+
const newData = xhr.responseText.substring(lastProcessedIndex);
|
|
285
|
+
lastProcessedIndex = xhr.responseText.length;
|
|
286
|
+
const dataToProcess = incompleteLineBuffer + newData;
|
|
287
|
+
incompleteLineBuffer = "";
|
|
288
|
+
const lines = dataToProcess.split("\n");
|
|
289
|
+
if (!newData.endsWith("\n") && lines.length > 0) {
|
|
290
|
+
incompleteLineBuffer = lines.pop() || "";
|
|
291
|
+
}
|
|
292
|
+
processSSELines(lines, accumulator, onData, globalOnData, onThinking, globalOnThinking);
|
|
293
|
+
};
|
|
294
|
+
xhr.onload = () => {
|
|
295
|
+
abortController.signal.removeEventListener("abort", abortHandler);
|
|
296
|
+
if (incompleteLineBuffer) {
|
|
297
|
+
processSSELines(
|
|
298
|
+
[incompleteLineBuffer.trim()],
|
|
299
|
+
accumulator,
|
|
300
|
+
onData,
|
|
301
|
+
globalOnData,
|
|
302
|
+
onThinking,
|
|
303
|
+
globalOnThinking
|
|
304
|
+
);
|
|
305
|
+
incompleteLineBuffer = "";
|
|
306
|
+
}
|
|
307
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
308
|
+
const response = buildResponseResponse(accumulator);
|
|
309
|
+
setIsLoading(false);
|
|
310
|
+
if (onFinish) onFinish(response);
|
|
311
|
+
resolve({ data: response, error: null });
|
|
312
|
+
} else {
|
|
313
|
+
const errorMsg = `Request failed with status ${xhr.status}`;
|
|
314
|
+
setIsLoading(false);
|
|
315
|
+
if (onError) onError(new Error(errorMsg));
|
|
316
|
+
resolve({ data: null, error: errorMsg });
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
xhr.onerror = () => {
|
|
320
|
+
abortController.signal.removeEventListener("abort", abortHandler);
|
|
321
|
+
const errorMsg = "Network error";
|
|
322
|
+
setIsLoading(false);
|
|
323
|
+
if (onError) onError(new Error(errorMsg));
|
|
324
|
+
resolve({ data: null, error: errorMsg });
|
|
325
|
+
};
|
|
326
|
+
xhr.onabort = () => {
|
|
327
|
+
abortController.signal.removeEventListener("abort", abortHandler);
|
|
328
|
+
setIsLoading(false);
|
|
329
|
+
resolve({ data: null, error: "Request aborted" });
|
|
330
|
+
};
|
|
331
|
+
xhr.send(
|
|
332
|
+
JSON.stringify({
|
|
333
|
+
input: messagesToInput(messages),
|
|
334
|
+
model,
|
|
335
|
+
stream: true,
|
|
336
|
+
// Responses API options (only include if defined)
|
|
337
|
+
...store !== void 0 && { store },
|
|
338
|
+
...previousResponseId && { previous_response_id: previousResponseId },
|
|
339
|
+
...conversation && { conversation },
|
|
340
|
+
...temperature !== void 0 && { temperature },
|
|
341
|
+
...maxOutputTokens !== void 0 && { max_output_tokens: maxOutputTokens },
|
|
342
|
+
...tools && { tools },
|
|
343
|
+
...toolChoice && { tool_choice: toolChoice },
|
|
344
|
+
...reasoning && { reasoning },
|
|
345
|
+
...thinking && { thinking }
|
|
346
|
+
})
|
|
347
|
+
);
|
|
348
|
+
});
|
|
349
|
+
return result;
|
|
350
|
+
} catch (err) {
|
|
351
|
+
setIsLoading(false);
|
|
352
|
+
return handleError(err, onError);
|
|
353
|
+
} finally {
|
|
354
|
+
if (abortControllerRef.current === abortController) {
|
|
355
|
+
abortControllerRef.current = null;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
[getToken, baseUrl, globalOnData, globalOnThinking, onFinish, onError]
|
|
360
|
+
);
|
|
361
|
+
return {
|
|
362
|
+
isLoading,
|
|
363
|
+
sendMessage,
|
|
364
|
+
stop
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// src/expo/useChatStorage.ts
|
|
369
|
+
import { useCallback as useCallback2, useState as useState2, useMemo } from "react";
|
|
370
|
+
|
|
371
|
+
// src/lib/db/chat/schema.ts
|
|
372
|
+
import { appSchema, tableSchema } from "@nozbe/watermelondb";
|
|
373
|
+
import {
|
|
374
|
+
schemaMigrations,
|
|
375
|
+
addColumns
|
|
376
|
+
} from "@nozbe/watermelondb/Schema/migrations";
|
|
377
|
+
var chatStorageSchema = appSchema({
|
|
378
|
+
version: 5,
|
|
379
|
+
tables: [
|
|
380
|
+
tableSchema({
|
|
381
|
+
name: "history",
|
|
382
|
+
columns: [
|
|
383
|
+
{ name: "message_id", type: "number" },
|
|
384
|
+
{ name: "conversation_id", type: "string", isIndexed: true },
|
|
385
|
+
{ name: "role", type: "string", isIndexed: true },
|
|
386
|
+
{ name: "content", type: "string" },
|
|
387
|
+
{ name: "model", type: "string", isOptional: true },
|
|
388
|
+
{ name: "files", type: "string", isOptional: true },
|
|
389
|
+
{ name: "created_at", type: "number", isIndexed: true },
|
|
390
|
+
{ name: "updated_at", type: "number" },
|
|
391
|
+
{ name: "vector", type: "string", isOptional: true },
|
|
392
|
+
{ name: "embedding_model", type: "string", isOptional: true },
|
|
393
|
+
{ name: "usage", type: "string", isOptional: true },
|
|
394
|
+
{ name: "sources", type: "string", isOptional: true },
|
|
395
|
+
{ name: "response_duration", type: "number", isOptional: true },
|
|
396
|
+
{ name: "was_stopped", type: "boolean", isOptional: true },
|
|
397
|
+
{ name: "error", type: "string", isOptional: true },
|
|
398
|
+
{ name: "thought_process", type: "string", isOptional: true },
|
|
399
|
+
// JSON stringified ActivityPhase[]
|
|
400
|
+
{ name: "thinking", type: "string", isOptional: true }
|
|
401
|
+
// Reasoning/thinking content
|
|
402
|
+
]
|
|
403
|
+
}),
|
|
404
|
+
tableSchema({
|
|
405
|
+
name: "conversations",
|
|
406
|
+
columns: [
|
|
407
|
+
{ name: "conversation_id", type: "string", isIndexed: true },
|
|
408
|
+
{ name: "title", type: "string" },
|
|
409
|
+
{ name: "created_at", type: "number" },
|
|
410
|
+
{ name: "updated_at", type: "number" },
|
|
411
|
+
{ name: "is_deleted", type: "boolean", isIndexed: true }
|
|
412
|
+
]
|
|
413
|
+
})
|
|
414
|
+
]
|
|
415
|
+
});
|
|
416
|
+
var chatStorageMigrations = schemaMigrations({
|
|
417
|
+
migrations: [
|
|
418
|
+
{
|
|
419
|
+
toVersion: 2,
|
|
420
|
+
steps: [
|
|
421
|
+
addColumns({
|
|
422
|
+
table: "history",
|
|
423
|
+
columns: [{ name: "was_stopped", type: "boolean", isOptional: true }]
|
|
424
|
+
})
|
|
425
|
+
]
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
toVersion: 3,
|
|
429
|
+
steps: [
|
|
430
|
+
addColumns({
|
|
431
|
+
table: "history",
|
|
432
|
+
columns: [{ name: "error", type: "string", isOptional: true }]
|
|
433
|
+
})
|
|
434
|
+
]
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
toVersion: 4,
|
|
438
|
+
steps: [
|
|
439
|
+
addColumns({
|
|
440
|
+
table: "history",
|
|
441
|
+
columns: [
|
|
442
|
+
{ name: "thought_process", type: "string", isOptional: true }
|
|
443
|
+
]
|
|
444
|
+
})
|
|
445
|
+
]
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
toVersion: 5,
|
|
449
|
+
steps: [
|
|
450
|
+
addColumns({
|
|
451
|
+
table: "history",
|
|
452
|
+
columns: [
|
|
453
|
+
{ name: "thinking", type: "string", isOptional: true }
|
|
454
|
+
]
|
|
455
|
+
})
|
|
456
|
+
]
|
|
457
|
+
}
|
|
458
|
+
]
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
// src/lib/db/chat/models.ts
|
|
462
|
+
import { Model } from "@nozbe/watermelondb";
|
|
463
|
+
import { field, text, date, json } from "@nozbe/watermelondb/decorators";
|
|
464
|
+
var Message = class extends Model {
|
|
465
|
+
};
|
|
466
|
+
Message.table = "history";
|
|
467
|
+
Message.associations = {
|
|
468
|
+
conversations: { type: "belongs_to", key: "conversation_id" }
|
|
469
|
+
};
|
|
470
|
+
__decorateClass([
|
|
471
|
+
field("message_id")
|
|
472
|
+
], Message.prototype, "messageId", 2);
|
|
473
|
+
__decorateClass([
|
|
474
|
+
text("conversation_id")
|
|
475
|
+
], Message.prototype, "conversationId", 2);
|
|
476
|
+
__decorateClass([
|
|
477
|
+
text("role")
|
|
478
|
+
], Message.prototype, "role", 2);
|
|
479
|
+
__decorateClass([
|
|
480
|
+
text("content")
|
|
481
|
+
], Message.prototype, "content", 2);
|
|
482
|
+
__decorateClass([
|
|
483
|
+
text("model")
|
|
484
|
+
], Message.prototype, "model", 2);
|
|
485
|
+
__decorateClass([
|
|
486
|
+
json("files", (json3) => json3)
|
|
487
|
+
], Message.prototype, "files", 2);
|
|
488
|
+
__decorateClass([
|
|
489
|
+
date("created_at")
|
|
490
|
+
], Message.prototype, "createdAt", 2);
|
|
491
|
+
__decorateClass([
|
|
492
|
+
date("updated_at")
|
|
493
|
+
], Message.prototype, "updatedAt", 2);
|
|
494
|
+
__decorateClass([
|
|
495
|
+
json("vector", (json3) => json3)
|
|
496
|
+
], Message.prototype, "vector", 2);
|
|
497
|
+
__decorateClass([
|
|
498
|
+
text("embedding_model")
|
|
499
|
+
], Message.prototype, "embeddingModel", 2);
|
|
500
|
+
__decorateClass([
|
|
501
|
+
json("usage", (json3) => json3)
|
|
502
|
+
], Message.prototype, "usage", 2);
|
|
503
|
+
__decorateClass([
|
|
504
|
+
json("sources", (json3) => json3)
|
|
505
|
+
], Message.prototype, "sources", 2);
|
|
506
|
+
__decorateClass([
|
|
507
|
+
field("response_duration")
|
|
508
|
+
], Message.prototype, "responseDuration", 2);
|
|
509
|
+
__decorateClass([
|
|
510
|
+
field("was_stopped")
|
|
511
|
+
], Message.prototype, "wasStopped", 2);
|
|
512
|
+
__decorateClass([
|
|
513
|
+
text("error")
|
|
514
|
+
], Message.prototype, "error", 2);
|
|
515
|
+
__decorateClass([
|
|
516
|
+
json("thought_process", (json3) => json3)
|
|
517
|
+
], Message.prototype, "thoughtProcess", 2);
|
|
518
|
+
__decorateClass([
|
|
519
|
+
text("thinking")
|
|
520
|
+
], Message.prototype, "thinking", 2);
|
|
521
|
+
var Conversation = class extends Model {
|
|
522
|
+
};
|
|
523
|
+
Conversation.table = "conversations";
|
|
524
|
+
Conversation.associations = {
|
|
525
|
+
history: { type: "has_many", foreignKey: "conversation_id" }
|
|
526
|
+
};
|
|
527
|
+
__decorateClass([
|
|
528
|
+
text("conversation_id")
|
|
529
|
+
], Conversation.prototype, "conversationId", 2);
|
|
530
|
+
__decorateClass([
|
|
531
|
+
text("title")
|
|
532
|
+
], Conversation.prototype, "title", 2);
|
|
533
|
+
__decorateClass([
|
|
534
|
+
date("created_at")
|
|
535
|
+
], Conversation.prototype, "createdAt", 2);
|
|
536
|
+
__decorateClass([
|
|
537
|
+
date("updated_at")
|
|
538
|
+
], Conversation.prototype, "updatedAt", 2);
|
|
539
|
+
__decorateClass([
|
|
540
|
+
field("is_deleted")
|
|
541
|
+
], Conversation.prototype, "isDeleted", 2);
|
|
542
|
+
|
|
543
|
+
// src/lib/db/chat/types.ts
|
|
544
|
+
function generateConversationId() {
|
|
545
|
+
return `conv_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
546
|
+
}
|
|
547
|
+
function convertUsageToStored(usage) {
|
|
548
|
+
if (!usage) return void 0;
|
|
549
|
+
return {
|
|
550
|
+
promptTokens: usage.prompt_tokens,
|
|
551
|
+
completionTokens: usage.completion_tokens,
|
|
552
|
+
totalTokens: usage.total_tokens,
|
|
553
|
+
costMicroUsd: usage.cost_micro_usd
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
function finalizeThoughtProcess(thoughtProcess) {
|
|
557
|
+
if (!thoughtProcess?.length) return thoughtProcess;
|
|
558
|
+
return thoughtProcess.map(
|
|
559
|
+
(phase, idx) => idx === thoughtProcess.length - 1 ? { ...phase, status: "completed" } : phase
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// src/lib/db/chat/operations.ts
|
|
564
|
+
import { Q } from "@nozbe/watermelondb";
|
|
565
|
+
function messageToStored(message) {
|
|
566
|
+
return {
|
|
567
|
+
uniqueId: message.id,
|
|
568
|
+
messageId: message.messageId,
|
|
569
|
+
conversationId: message.conversationId,
|
|
570
|
+
role: message.role,
|
|
571
|
+
content: message.content,
|
|
572
|
+
model: message.model,
|
|
573
|
+
files: message.files,
|
|
574
|
+
createdAt: message.createdAt,
|
|
575
|
+
updatedAt: message.updatedAt,
|
|
576
|
+
vector: message.vector,
|
|
577
|
+
embeddingModel: message.embeddingModel,
|
|
578
|
+
usage: message.usage,
|
|
579
|
+
sources: message.sources,
|
|
580
|
+
responseDuration: message.responseDuration,
|
|
581
|
+
wasStopped: message.wasStopped,
|
|
582
|
+
error: message.error,
|
|
583
|
+
thoughtProcess: message.thoughtProcess,
|
|
584
|
+
thinking: message.thinking
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
function conversationToStored(conversation) {
|
|
588
|
+
return {
|
|
589
|
+
uniqueId: conversation.id,
|
|
590
|
+
conversationId: conversation.conversationId,
|
|
591
|
+
title: conversation.title,
|
|
592
|
+
createdAt: conversation.createdAt,
|
|
593
|
+
updatedAt: conversation.updatedAt,
|
|
594
|
+
isDeleted: conversation.isDeleted
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
async function createConversationOp(ctx, opts, defaultTitle = "New Conversation") {
|
|
598
|
+
const convId = opts?.conversationId || generateConversationId();
|
|
599
|
+
const title = opts?.title || defaultTitle;
|
|
600
|
+
const created = await ctx.database.write(async () => {
|
|
601
|
+
return await ctx.conversationsCollection.create((conv) => {
|
|
602
|
+
conv._setRaw("conversation_id", convId);
|
|
603
|
+
conv._setRaw("title", title);
|
|
604
|
+
conv._setRaw("is_deleted", false);
|
|
605
|
+
});
|
|
606
|
+
});
|
|
607
|
+
return conversationToStored(created);
|
|
608
|
+
}
|
|
609
|
+
async function getConversationOp(ctx, id) {
|
|
610
|
+
const results = await ctx.conversationsCollection.query(Q.where("conversation_id", id), Q.where("is_deleted", false)).fetch();
|
|
611
|
+
return results.length > 0 ? conversationToStored(results[0]) : null;
|
|
612
|
+
}
|
|
613
|
+
async function getConversationsOp(ctx) {
|
|
614
|
+
const results = await ctx.conversationsCollection.query(Q.where("is_deleted", false), Q.sortBy("created_at", Q.desc)).fetch();
|
|
615
|
+
return results.map(conversationToStored);
|
|
616
|
+
}
|
|
617
|
+
async function updateConversationTitleOp(ctx, id, title) {
|
|
618
|
+
const results = await ctx.conversationsCollection.query(Q.where("conversation_id", id), Q.where("is_deleted", false)).fetch();
|
|
619
|
+
if (results.length > 0) {
|
|
620
|
+
await ctx.database.write(async () => {
|
|
621
|
+
await results[0].update((conv) => {
|
|
622
|
+
conv._setRaw("title", title);
|
|
623
|
+
});
|
|
624
|
+
});
|
|
625
|
+
return true;
|
|
626
|
+
}
|
|
627
|
+
return false;
|
|
628
|
+
}
|
|
629
|
+
async function deleteConversationOp(ctx, id) {
|
|
630
|
+
const results = await ctx.conversationsCollection.query(Q.where("conversation_id", id), Q.where("is_deleted", false)).fetch();
|
|
631
|
+
if (results.length > 0) {
|
|
632
|
+
await ctx.database.write(async () => {
|
|
633
|
+
await results[0].update((conv) => {
|
|
634
|
+
conv._setRaw("is_deleted", true);
|
|
635
|
+
});
|
|
636
|
+
});
|
|
637
|
+
return true;
|
|
638
|
+
}
|
|
639
|
+
return false;
|
|
640
|
+
}
|
|
641
|
+
async function getMessagesOp(ctx, convId) {
|
|
642
|
+
const results = await ctx.messagesCollection.query(Q.where("conversation_id", convId), Q.sortBy("message_id", Q.asc)).fetch();
|
|
643
|
+
return results.map(messageToStored);
|
|
644
|
+
}
|
|
645
|
+
async function getMessageCountOp(ctx, convId) {
|
|
646
|
+
return await ctx.messagesCollection.query(Q.where("conversation_id", convId)).fetchCount();
|
|
647
|
+
}
|
|
648
|
+
async function clearMessagesOp(ctx, convId) {
|
|
649
|
+
const messages = await ctx.messagesCollection.query(Q.where("conversation_id", convId)).fetch();
|
|
650
|
+
await ctx.database.write(async () => {
|
|
651
|
+
for (const message of messages) {
|
|
652
|
+
await message.destroyPermanently();
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
async function createMessageOp(ctx, opts) {
|
|
657
|
+
const existingCount = await getMessageCountOp(ctx, opts.conversationId);
|
|
658
|
+
const messageId = existingCount + 1;
|
|
659
|
+
const created = await ctx.database.write(async () => {
|
|
660
|
+
return await ctx.messagesCollection.create((msg) => {
|
|
661
|
+
msg._setRaw("message_id", messageId);
|
|
662
|
+
msg._setRaw("conversation_id", opts.conversationId);
|
|
663
|
+
msg._setRaw("role", opts.role);
|
|
664
|
+
msg._setRaw("content", opts.content);
|
|
665
|
+
if (opts.model) msg._setRaw("model", opts.model);
|
|
666
|
+
if (opts.files) msg._setRaw("files", JSON.stringify(opts.files));
|
|
667
|
+
if (opts.usage) msg._setRaw("usage", JSON.stringify(opts.usage));
|
|
668
|
+
if (opts.sources) msg._setRaw("sources", JSON.stringify(opts.sources));
|
|
669
|
+
if (opts.responseDuration !== void 0)
|
|
670
|
+
msg._setRaw("response_duration", opts.responseDuration);
|
|
671
|
+
if (opts.vector) msg._setRaw("vector", JSON.stringify(opts.vector));
|
|
672
|
+
if (opts.embeddingModel)
|
|
673
|
+
msg._setRaw("embedding_model", opts.embeddingModel);
|
|
674
|
+
if (opts.wasStopped) msg._setRaw("was_stopped", opts.wasStopped);
|
|
675
|
+
if (opts.error) msg._setRaw("error", opts.error);
|
|
676
|
+
if (opts.thoughtProcess)
|
|
677
|
+
msg._setRaw("thought_process", JSON.stringify(opts.thoughtProcess));
|
|
678
|
+
if (opts.thinking) msg._setRaw("thinking", opts.thinking);
|
|
679
|
+
});
|
|
680
|
+
});
|
|
681
|
+
return messageToStored(created);
|
|
682
|
+
}
|
|
683
|
+
async function updateMessageErrorOp(ctx, uniqueId, error) {
|
|
684
|
+
let message;
|
|
685
|
+
try {
|
|
686
|
+
message = await ctx.messagesCollection.find(uniqueId);
|
|
687
|
+
} catch {
|
|
688
|
+
return null;
|
|
689
|
+
}
|
|
690
|
+
await ctx.database.write(async () => {
|
|
691
|
+
await message.update((msg) => {
|
|
692
|
+
msg._setRaw("error", error);
|
|
693
|
+
});
|
|
694
|
+
});
|
|
695
|
+
return messageToStored(message);
|
|
696
|
+
}
|
|
697
|
+
async function updateMessageOp(ctx, uniqueId, opts) {
|
|
698
|
+
let message;
|
|
699
|
+
try {
|
|
700
|
+
message = await ctx.messagesCollection.find(uniqueId);
|
|
701
|
+
} catch {
|
|
702
|
+
return null;
|
|
703
|
+
}
|
|
704
|
+
await ctx.database.write(async () => {
|
|
705
|
+
await message.update((msg) => {
|
|
706
|
+
if (opts.content !== void 0) msg._setRaw("content", opts.content);
|
|
707
|
+
if (opts.model !== void 0) msg._setRaw("model", opts.model);
|
|
708
|
+
if (opts.files !== void 0)
|
|
709
|
+
msg._setRaw("files", JSON.stringify(opts.files));
|
|
710
|
+
if (opts.usage !== void 0)
|
|
711
|
+
msg._setRaw("usage", JSON.stringify(opts.usage));
|
|
712
|
+
if (opts.sources !== void 0)
|
|
713
|
+
msg._setRaw("sources", JSON.stringify(opts.sources));
|
|
714
|
+
if (opts.responseDuration !== void 0)
|
|
715
|
+
msg._setRaw("response_duration", opts.responseDuration);
|
|
716
|
+
if (opts.vector !== void 0)
|
|
717
|
+
msg._setRaw("vector", JSON.stringify(opts.vector));
|
|
718
|
+
if (opts.embeddingModel !== void 0)
|
|
719
|
+
msg._setRaw("embedding_model", opts.embeddingModel);
|
|
720
|
+
if (opts.wasStopped !== void 0)
|
|
721
|
+
msg._setRaw("was_stopped", opts.wasStopped);
|
|
722
|
+
if (opts.error !== void 0)
|
|
723
|
+
msg._setRaw("error", opts.error === null ? "" : opts.error);
|
|
724
|
+
if (opts.thoughtProcess !== void 0)
|
|
725
|
+
msg._setRaw("thought_process", JSON.stringify(opts.thoughtProcess));
|
|
726
|
+
if (opts.thinking !== void 0)
|
|
727
|
+
msg._setRaw("thinking", opts.thinking === null ? "" : opts.thinking);
|
|
728
|
+
});
|
|
729
|
+
});
|
|
730
|
+
return messageToStored(message);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// src/expo/useChatStorage.ts
|
|
734
|
+
function storedToLlmapiMessage(stored) {
|
|
735
|
+
const content = [
|
|
736
|
+
{ type: "text", text: stored.content }
|
|
737
|
+
];
|
|
738
|
+
if (stored.files?.length) {
|
|
739
|
+
for (const file of stored.files) {
|
|
740
|
+
if (file.url) {
|
|
741
|
+
content.push({
|
|
742
|
+
type: "image_url",
|
|
743
|
+
image_url: { url: file.url }
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
return {
|
|
749
|
+
role: stored.role,
|
|
750
|
+
content
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
function useChatStorage(options) {
|
|
754
|
+
const {
|
|
755
|
+
database,
|
|
756
|
+
conversationId: initialConversationId,
|
|
757
|
+
autoCreateConversation = true,
|
|
758
|
+
defaultConversationTitle = "New Conversation",
|
|
759
|
+
getToken,
|
|
760
|
+
baseUrl,
|
|
761
|
+
onData,
|
|
762
|
+
onFinish,
|
|
763
|
+
onError
|
|
764
|
+
} = options;
|
|
765
|
+
const [currentConversationId, setCurrentConversationId] = useState2(initialConversationId || null);
|
|
766
|
+
const messagesCollection = useMemo(
|
|
767
|
+
() => database.get("history"),
|
|
768
|
+
[database]
|
|
769
|
+
);
|
|
770
|
+
const conversationsCollection = useMemo(
|
|
771
|
+
() => database.get("conversations"),
|
|
772
|
+
[database]
|
|
773
|
+
);
|
|
774
|
+
const storageCtx = useMemo(
|
|
775
|
+
() => ({
|
|
776
|
+
database,
|
|
777
|
+
messagesCollection,
|
|
778
|
+
conversationsCollection
|
|
779
|
+
}),
|
|
780
|
+
[database, messagesCollection, conversationsCollection]
|
|
781
|
+
);
|
|
782
|
+
const {
|
|
783
|
+
isLoading,
|
|
784
|
+
sendMessage: baseSendMessage,
|
|
785
|
+
stop
|
|
786
|
+
} = useChat({
|
|
787
|
+
getToken,
|
|
788
|
+
baseUrl,
|
|
789
|
+
onData,
|
|
790
|
+
onFinish,
|
|
791
|
+
onError
|
|
792
|
+
});
|
|
793
|
+
const createConversation = useCallback2(
|
|
794
|
+
async (opts) => {
|
|
795
|
+
const created = await createConversationOp(
|
|
796
|
+
storageCtx,
|
|
797
|
+
opts,
|
|
798
|
+
defaultConversationTitle
|
|
799
|
+
);
|
|
800
|
+
setCurrentConversationId(created.conversationId);
|
|
801
|
+
return created;
|
|
802
|
+
},
|
|
803
|
+
[storageCtx, defaultConversationTitle]
|
|
804
|
+
);
|
|
805
|
+
const getConversation = useCallback2(
|
|
806
|
+
async (id) => {
|
|
807
|
+
return getConversationOp(storageCtx, id);
|
|
808
|
+
},
|
|
809
|
+
[storageCtx]
|
|
810
|
+
);
|
|
811
|
+
const getConversations = useCallback2(async () => {
|
|
812
|
+
return getConversationsOp(storageCtx);
|
|
813
|
+
}, [storageCtx]);
|
|
814
|
+
const updateConversationTitle = useCallback2(
|
|
815
|
+
async (id, title) => {
|
|
816
|
+
return updateConversationTitleOp(storageCtx, id, title);
|
|
817
|
+
},
|
|
818
|
+
[storageCtx]
|
|
819
|
+
);
|
|
820
|
+
const deleteConversation = useCallback2(
|
|
821
|
+
async (id) => {
|
|
822
|
+
const deleted = await deleteConversationOp(storageCtx, id);
|
|
823
|
+
if (deleted && currentConversationId === id) {
|
|
824
|
+
setCurrentConversationId(null);
|
|
825
|
+
}
|
|
826
|
+
return deleted;
|
|
827
|
+
},
|
|
828
|
+
[storageCtx, currentConversationId]
|
|
829
|
+
);
|
|
830
|
+
const getMessages = useCallback2(
|
|
831
|
+
async (convId) => {
|
|
832
|
+
return getMessagesOp(storageCtx, convId);
|
|
833
|
+
},
|
|
834
|
+
[storageCtx]
|
|
835
|
+
);
|
|
836
|
+
const getMessageCount = useCallback2(
|
|
837
|
+
async (convId) => {
|
|
838
|
+
return getMessageCountOp(storageCtx, convId);
|
|
839
|
+
},
|
|
840
|
+
[storageCtx]
|
|
841
|
+
);
|
|
842
|
+
const clearMessages = useCallback2(
|
|
843
|
+
async (convId) => {
|
|
844
|
+
return clearMessagesOp(storageCtx, convId);
|
|
845
|
+
},
|
|
846
|
+
[storageCtx]
|
|
847
|
+
);
|
|
848
|
+
const updateMessage = useCallback2(
|
|
849
|
+
async (uniqueId, options2) => {
|
|
850
|
+
return updateMessageOp(storageCtx, uniqueId, options2);
|
|
851
|
+
},
|
|
852
|
+
[storageCtx]
|
|
853
|
+
);
|
|
854
|
+
const extractSourcesFromAssistantMessage = useCallback2(
|
|
855
|
+
(assistantMessage) => {
|
|
856
|
+
try {
|
|
857
|
+
const extractedSources = [];
|
|
858
|
+
const seenUrls = /* @__PURE__ */ new Set();
|
|
859
|
+
if (assistantMessage.sources) {
|
|
860
|
+
for (const source of assistantMessage.sources) {
|
|
861
|
+
if (source.url) {
|
|
862
|
+
seenUrls.add(source.url);
|
|
863
|
+
}
|
|
864
|
+
extractedSources.push(source);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
const content = assistantMessage.content;
|
|
868
|
+
if (!content) {
|
|
869
|
+
return extractedSources;
|
|
870
|
+
}
|
|
871
|
+
const markdownLinkRegex = /\[([^\]]*)\]\(([^()]*(?:\([^()]*\)[^()]*)*)\)/g;
|
|
872
|
+
const plainUrlRegex = /https?:\/\/[^\s<>\[\]()'"]+/g;
|
|
873
|
+
let match;
|
|
874
|
+
while ((match = markdownLinkRegex.exec(content)) !== null) {
|
|
875
|
+
const title = match[1].trim();
|
|
876
|
+
const url = match[2].trim();
|
|
877
|
+
if (url && !seenUrls.has(url)) {
|
|
878
|
+
seenUrls.add(url);
|
|
879
|
+
extractedSources.push({
|
|
880
|
+
title: title || void 0,
|
|
881
|
+
url
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
while ((match = plainUrlRegex.exec(content)) !== null) {
|
|
886
|
+
const url = match[0].replace(/[.,;:!?]+$/, "").trim();
|
|
887
|
+
if (url && !seenUrls.has(url)) {
|
|
888
|
+
seenUrls.add(url);
|
|
889
|
+
try {
|
|
890
|
+
const urlObj = new URL(url);
|
|
891
|
+
extractedSources.push({
|
|
892
|
+
title: urlObj.hostname,
|
|
893
|
+
url
|
|
894
|
+
});
|
|
895
|
+
} catch {
|
|
896
|
+
extractedSources.push({
|
|
897
|
+
url
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
return extractedSources;
|
|
903
|
+
} catch {
|
|
904
|
+
return [];
|
|
905
|
+
}
|
|
906
|
+
},
|
|
907
|
+
[]
|
|
908
|
+
);
|
|
909
|
+
const ensureConversation = useCallback2(async () => {
|
|
910
|
+
if (currentConversationId) {
|
|
911
|
+
const existing = await getConversation(currentConversationId);
|
|
912
|
+
if (existing) {
|
|
913
|
+
return currentConversationId;
|
|
914
|
+
}
|
|
915
|
+
if (autoCreateConversation) {
|
|
916
|
+
const newConv = await createConversation({
|
|
917
|
+
conversationId: currentConversationId
|
|
918
|
+
});
|
|
919
|
+
return newConv.conversationId;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
if (autoCreateConversation) {
|
|
923
|
+
const newConv = await createConversation();
|
|
924
|
+
return newConv.conversationId;
|
|
925
|
+
}
|
|
926
|
+
throw new Error(
|
|
927
|
+
"No conversation ID provided and autoCreateConversation is disabled"
|
|
928
|
+
);
|
|
929
|
+
}, [
|
|
930
|
+
currentConversationId,
|
|
931
|
+
getConversation,
|
|
932
|
+
autoCreateConversation,
|
|
933
|
+
createConversation
|
|
934
|
+
]);
|
|
935
|
+
const sendMessage = useCallback2(
|
|
936
|
+
async (args) => {
|
|
937
|
+
const {
|
|
938
|
+
content,
|
|
939
|
+
model,
|
|
940
|
+
messages: providedMessages,
|
|
941
|
+
includeHistory = true,
|
|
942
|
+
maxHistoryMessages = 50,
|
|
943
|
+
files,
|
|
944
|
+
onData: perRequestOnData,
|
|
945
|
+
onThinking: perRequestOnThinking,
|
|
946
|
+
sources,
|
|
947
|
+
thoughtProcess,
|
|
948
|
+
// Responses API options
|
|
949
|
+
store,
|
|
950
|
+
previousResponseId,
|
|
951
|
+
serverConversation,
|
|
952
|
+
temperature,
|
|
953
|
+
maxOutputTokens,
|
|
954
|
+
tools,
|
|
955
|
+
toolChoice,
|
|
956
|
+
reasoning,
|
|
957
|
+
thinking
|
|
958
|
+
} = args;
|
|
959
|
+
let convId;
|
|
960
|
+
try {
|
|
961
|
+
convId = await ensureConversation();
|
|
962
|
+
} catch (err) {
|
|
963
|
+
return {
|
|
964
|
+
data: null,
|
|
965
|
+
error: err instanceof Error ? err.message : "Failed to ensure conversation"
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
let messagesToSend = [];
|
|
969
|
+
if (includeHistory && !providedMessages) {
|
|
970
|
+
const storedMessages = await getMessages(convId);
|
|
971
|
+
const validMessages = storedMessages.filter((msg) => !msg.error);
|
|
972
|
+
const limitedMessages = validMessages.slice(-maxHistoryMessages);
|
|
973
|
+
messagesToSend = limitedMessages.map(storedToLlmapiMessage);
|
|
974
|
+
} else if (providedMessages) {
|
|
975
|
+
messagesToSend = providedMessages;
|
|
976
|
+
}
|
|
977
|
+
const userMessageContent = [
|
|
978
|
+
{ type: "text", text: content }
|
|
979
|
+
];
|
|
980
|
+
if (files?.length) {
|
|
981
|
+
for (const file of files) {
|
|
982
|
+
if (file.url) {
|
|
983
|
+
userMessageContent.push({
|
|
984
|
+
type: "image_url",
|
|
985
|
+
image_url: { url: file.url }
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
const userMessage = {
|
|
991
|
+
role: "user",
|
|
992
|
+
content: userMessageContent
|
|
993
|
+
};
|
|
994
|
+
messagesToSend.push(userMessage);
|
|
995
|
+
const sanitizedFiles = files?.map((file) => ({
|
|
996
|
+
id: file.id,
|
|
997
|
+
name: file.name,
|
|
998
|
+
type: file.type,
|
|
999
|
+
size: file.size,
|
|
1000
|
+
// Only keep URL if it's not a data URI (e.g., external URLs)
|
|
1001
|
+
url: file.url && !file.url.startsWith("data:") ? file.url : void 0
|
|
1002
|
+
}));
|
|
1003
|
+
let storedUserMessage;
|
|
1004
|
+
try {
|
|
1005
|
+
storedUserMessage = await createMessageOp(storageCtx, {
|
|
1006
|
+
conversationId: convId,
|
|
1007
|
+
role: "user",
|
|
1008
|
+
content,
|
|
1009
|
+
files: sanitizedFiles,
|
|
1010
|
+
model
|
|
1011
|
+
});
|
|
1012
|
+
} catch (err) {
|
|
1013
|
+
return {
|
|
1014
|
+
data: null,
|
|
1015
|
+
error: err instanceof Error ? err.message : "Failed to store user message"
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
const startTime = Date.now();
|
|
1019
|
+
const result = await baseSendMessage({
|
|
1020
|
+
messages: messagesToSend,
|
|
1021
|
+
model,
|
|
1022
|
+
onData: perRequestOnData,
|
|
1023
|
+
onThinking: perRequestOnThinking,
|
|
1024
|
+
// Responses API options
|
|
1025
|
+
store,
|
|
1026
|
+
previousResponseId,
|
|
1027
|
+
conversation: serverConversation,
|
|
1028
|
+
temperature,
|
|
1029
|
+
maxOutputTokens,
|
|
1030
|
+
tools,
|
|
1031
|
+
toolChoice,
|
|
1032
|
+
reasoning,
|
|
1033
|
+
thinking
|
|
1034
|
+
});
|
|
1035
|
+
const responseDuration = (Date.now() - startTime) / 1e3;
|
|
1036
|
+
if (result.error || !result.data) {
|
|
1037
|
+
const abortedResult = result;
|
|
1038
|
+
if (abortedResult.error === "Request aborted") {
|
|
1039
|
+
const assistantContent2 = abortedResult.data?.output?.[0]?.content?.map((part) => part.text || "").join("") || "";
|
|
1040
|
+
const responseModel = abortedResult.data?.model || model || "";
|
|
1041
|
+
let storedAssistantMessage2;
|
|
1042
|
+
try {
|
|
1043
|
+
storedAssistantMessage2 = await createMessageOp(storageCtx, {
|
|
1044
|
+
conversationId: convId,
|
|
1045
|
+
role: "assistant",
|
|
1046
|
+
content: assistantContent2,
|
|
1047
|
+
model: responseModel,
|
|
1048
|
+
usage: convertUsageToStored(abortedResult.data?.usage),
|
|
1049
|
+
responseDuration,
|
|
1050
|
+
wasStopped: true,
|
|
1051
|
+
sources,
|
|
1052
|
+
thoughtProcess: finalizeThoughtProcess(thoughtProcess)
|
|
1053
|
+
});
|
|
1054
|
+
const responseData2 = abortedResult.data || {
|
|
1055
|
+
id: `aborted-${Date.now()}`,
|
|
1056
|
+
model: responseModel,
|
|
1057
|
+
object: "response",
|
|
1058
|
+
output: [{
|
|
1059
|
+
type: "message",
|
|
1060
|
+
role: "assistant",
|
|
1061
|
+
content: [{ type: "output_text", text: assistantContent2 }],
|
|
1062
|
+
status: "completed"
|
|
1063
|
+
}],
|
|
1064
|
+
usage: void 0
|
|
1065
|
+
};
|
|
1066
|
+
return {
|
|
1067
|
+
data: responseData2,
|
|
1068
|
+
error: null,
|
|
1069
|
+
// Treat as success to the caller
|
|
1070
|
+
userMessage: storedUserMessage,
|
|
1071
|
+
assistantMessage: storedAssistantMessage2
|
|
1072
|
+
};
|
|
1073
|
+
} catch {
|
|
1074
|
+
return {
|
|
1075
|
+
data: null,
|
|
1076
|
+
error: "Request aborted",
|
|
1077
|
+
userMessage: storedUserMessage
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
const errorMessage = result.error || "No response data received";
|
|
1082
|
+
try {
|
|
1083
|
+
await updateMessageErrorOp(
|
|
1084
|
+
storageCtx,
|
|
1085
|
+
storedUserMessage.uniqueId,
|
|
1086
|
+
errorMessage
|
|
1087
|
+
);
|
|
1088
|
+
await createMessageOp(storageCtx, {
|
|
1089
|
+
conversationId: convId,
|
|
1090
|
+
role: "assistant",
|
|
1091
|
+
content: "",
|
|
1092
|
+
model: model || "",
|
|
1093
|
+
responseDuration,
|
|
1094
|
+
sources,
|
|
1095
|
+
thoughtProcess: finalizeThoughtProcess(thoughtProcess),
|
|
1096
|
+
error: errorMessage
|
|
1097
|
+
});
|
|
1098
|
+
} catch {
|
|
1099
|
+
}
|
|
1100
|
+
return {
|
|
1101
|
+
data: null,
|
|
1102
|
+
error: errorMessage,
|
|
1103
|
+
userMessage: { ...storedUserMessage, error: errorMessage }
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
const responseData = result.data;
|
|
1107
|
+
const messageOutput = responseData.output?.find(
|
|
1108
|
+
(item) => item.type === "message"
|
|
1109
|
+
);
|
|
1110
|
+
const assistantContent = messageOutput?.content?.map((part) => part.text || "").join("") || "";
|
|
1111
|
+
const reasoningOutput = responseData.output?.find(
|
|
1112
|
+
(item) => item.type === "reasoning"
|
|
1113
|
+
);
|
|
1114
|
+
const thinkingContent = reasoningOutput?.content?.map((part) => part.text || "").join("") || void 0;
|
|
1115
|
+
const combinedSources = extractSourcesFromAssistantMessage({
|
|
1116
|
+
content: assistantContent,
|
|
1117
|
+
sources
|
|
1118
|
+
});
|
|
1119
|
+
let storedAssistantMessage;
|
|
1120
|
+
try {
|
|
1121
|
+
storedAssistantMessage = await createMessageOp(storageCtx, {
|
|
1122
|
+
conversationId: convId,
|
|
1123
|
+
role: "assistant",
|
|
1124
|
+
content: assistantContent,
|
|
1125
|
+
model: responseData.model || model,
|
|
1126
|
+
usage: convertUsageToStored(responseData.usage),
|
|
1127
|
+
responseDuration,
|
|
1128
|
+
sources: combinedSources,
|
|
1129
|
+
thoughtProcess: finalizeThoughtProcess(thoughtProcess),
|
|
1130
|
+
thinking: thinkingContent
|
|
1131
|
+
});
|
|
1132
|
+
} catch (err) {
|
|
1133
|
+
return {
|
|
1134
|
+
data: null,
|
|
1135
|
+
error: err instanceof Error ? err.message : "Failed to store assistant message",
|
|
1136
|
+
userMessage: storedUserMessage
|
|
1137
|
+
};
|
|
1138
|
+
}
|
|
1139
|
+
return {
|
|
1140
|
+
data: responseData,
|
|
1141
|
+
error: null,
|
|
1142
|
+
userMessage: storedUserMessage,
|
|
1143
|
+
assistantMessage: storedAssistantMessage
|
|
1144
|
+
};
|
|
1145
|
+
},
|
|
1146
|
+
[ensureConversation, getMessages, storageCtx, baseSendMessage]
|
|
1147
|
+
);
|
|
1148
|
+
return {
|
|
1149
|
+
isLoading,
|
|
1150
|
+
sendMessage,
|
|
1151
|
+
stop,
|
|
1152
|
+
conversationId: currentConversationId,
|
|
1153
|
+
setConversationId: setCurrentConversationId,
|
|
1154
|
+
createConversation,
|
|
1155
|
+
getConversation,
|
|
1156
|
+
getConversations,
|
|
1157
|
+
updateConversationTitle,
|
|
1158
|
+
deleteConversation,
|
|
1159
|
+
getMessages,
|
|
1160
|
+
getMessageCount,
|
|
1161
|
+
clearMessages,
|
|
1162
|
+
extractSourcesFromAssistantMessage,
|
|
1163
|
+
updateMessage
|
|
1164
|
+
};
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
// src/react/useImageGeneration.ts
|
|
1168
|
+
import { useCallback as useCallback3, useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
|
|
1169
|
+
|
|
1170
|
+
// src/client/core/bodySerializer.gen.ts
|
|
1171
|
+
var jsonBodySerializer = {
|
|
1172
|
+
bodySerializer: (body) => JSON.stringify(
|
|
1173
|
+
body,
|
|
1174
|
+
(_key, value) => typeof value === "bigint" ? value.toString() : value
|
|
1175
|
+
)
|
|
1176
|
+
};
|
|
1177
|
+
|
|
1178
|
+
// src/client/core/params.gen.ts
|
|
1179
|
+
var extraPrefixesMap = {
|
|
1180
|
+
$body_: "body",
|
|
1181
|
+
$headers_: "headers",
|
|
1182
|
+
$path_: "path",
|
|
1183
|
+
$query_: "query"
|
|
1184
|
+
};
|
|
1185
|
+
var extraPrefixes = Object.entries(extraPrefixesMap);
|
|
1186
|
+
|
|
1187
|
+
// src/client/core/serverSentEvents.gen.ts
|
|
1188
|
+
var createSseClient = ({
|
|
1189
|
+
onRequest,
|
|
1190
|
+
onSseError,
|
|
1191
|
+
onSseEvent,
|
|
1192
|
+
responseTransformer,
|
|
1193
|
+
responseValidator,
|
|
1194
|
+
sseDefaultRetryDelay,
|
|
1195
|
+
sseMaxRetryAttempts,
|
|
1196
|
+
sseMaxRetryDelay,
|
|
1197
|
+
sseSleepFn,
|
|
1198
|
+
url,
|
|
1199
|
+
...options
|
|
1200
|
+
}) => {
|
|
1201
|
+
let lastEventId;
|
|
1202
|
+
const sleep = sseSleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
1203
|
+
const createStream = async function* () {
|
|
1204
|
+
let retryDelay = sseDefaultRetryDelay ?? 3e3;
|
|
1205
|
+
let attempt = 0;
|
|
1206
|
+
const signal = options.signal ?? new AbortController().signal;
|
|
1207
|
+
while (true) {
|
|
1208
|
+
if (signal.aborted) break;
|
|
1209
|
+
attempt++;
|
|
1210
|
+
const headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers);
|
|
1211
|
+
if (lastEventId !== void 0) {
|
|
1212
|
+
headers.set("Last-Event-ID", lastEventId);
|
|
1213
|
+
}
|
|
1214
|
+
try {
|
|
1215
|
+
const requestInit = {
|
|
1216
|
+
redirect: "follow",
|
|
1217
|
+
...options,
|
|
1218
|
+
body: options.serializedBody,
|
|
1219
|
+
headers,
|
|
1220
|
+
signal
|
|
1221
|
+
};
|
|
1222
|
+
let request = new Request(url, requestInit);
|
|
1223
|
+
if (onRequest) {
|
|
1224
|
+
request = await onRequest(url, requestInit);
|
|
1225
|
+
}
|
|
1226
|
+
const _fetch = options.fetch ?? globalThis.fetch;
|
|
1227
|
+
const response = await _fetch(request);
|
|
1228
|
+
if (!response.ok)
|
|
1229
|
+
throw new Error(
|
|
1230
|
+
`SSE failed: ${response.status} ${response.statusText}`
|
|
1231
|
+
);
|
|
1232
|
+
if (!response.body) throw new Error("No body in SSE response");
|
|
1233
|
+
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
|
|
1234
|
+
let buffer = "";
|
|
1235
|
+
const abortHandler = () => {
|
|
1236
|
+
try {
|
|
1237
|
+
reader.cancel();
|
|
1238
|
+
} catch {
|
|
1239
|
+
}
|
|
1240
|
+
};
|
|
1241
|
+
signal.addEventListener("abort", abortHandler);
|
|
1242
|
+
try {
|
|
1243
|
+
while (true) {
|
|
1244
|
+
const { done, value } = await reader.read();
|
|
1245
|
+
if (done) break;
|
|
1246
|
+
buffer += value;
|
|
1247
|
+
const chunks = buffer.split("\n\n");
|
|
1248
|
+
buffer = chunks.pop() ?? "";
|
|
1249
|
+
for (const chunk of chunks) {
|
|
1250
|
+
const lines = chunk.split("\n");
|
|
1251
|
+
const dataLines = [];
|
|
1252
|
+
let eventName;
|
|
1253
|
+
for (const line of lines) {
|
|
1254
|
+
if (line.startsWith("data:")) {
|
|
1255
|
+
dataLines.push(line.replace(/^data:\s*/, ""));
|
|
1256
|
+
} else if (line.startsWith("event:")) {
|
|
1257
|
+
eventName = line.replace(/^event:\s*/, "");
|
|
1258
|
+
} else if (line.startsWith("id:")) {
|
|
1259
|
+
lastEventId = line.replace(/^id:\s*/, "");
|
|
1260
|
+
} else if (line.startsWith("retry:")) {
|
|
1261
|
+
const parsed = Number.parseInt(
|
|
1262
|
+
line.replace(/^retry:\s*/, ""),
|
|
1263
|
+
10
|
|
1264
|
+
);
|
|
1265
|
+
if (!Number.isNaN(parsed)) {
|
|
1266
|
+
retryDelay = parsed;
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
let data;
|
|
1271
|
+
let parsedJson = false;
|
|
1272
|
+
if (dataLines.length) {
|
|
1273
|
+
const rawData = dataLines.join("\n");
|
|
1274
|
+
try {
|
|
1275
|
+
data = JSON.parse(rawData);
|
|
1276
|
+
parsedJson = true;
|
|
1277
|
+
} catch {
|
|
1278
|
+
data = rawData;
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
if (parsedJson) {
|
|
1282
|
+
if (responseValidator) {
|
|
1283
|
+
await responseValidator(data);
|
|
1284
|
+
}
|
|
1285
|
+
if (responseTransformer) {
|
|
1286
|
+
data = await responseTransformer(data);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
onSseEvent?.({
|
|
1290
|
+
data,
|
|
1291
|
+
event: eventName,
|
|
1292
|
+
id: lastEventId,
|
|
1293
|
+
retry: retryDelay
|
|
1294
|
+
});
|
|
1295
|
+
if (dataLines.length) {
|
|
1296
|
+
yield data;
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
} finally {
|
|
1301
|
+
signal.removeEventListener("abort", abortHandler);
|
|
1302
|
+
reader.releaseLock();
|
|
1303
|
+
}
|
|
1304
|
+
break;
|
|
1305
|
+
} catch (error) {
|
|
1306
|
+
onSseError?.(error);
|
|
1307
|
+
if (sseMaxRetryAttempts !== void 0 && attempt >= sseMaxRetryAttempts) {
|
|
1308
|
+
break;
|
|
1309
|
+
}
|
|
1310
|
+
const backoff = Math.min(
|
|
1311
|
+
retryDelay * 2 ** (attempt - 1),
|
|
1312
|
+
sseMaxRetryDelay ?? 3e4
|
|
1313
|
+
);
|
|
1314
|
+
await sleep(backoff);
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
};
|
|
1318
|
+
const stream = createStream();
|
|
1319
|
+
return { stream };
|
|
1320
|
+
};
|
|
1321
|
+
|
|
1322
|
+
// src/client/core/pathSerializer.gen.ts
|
|
1323
|
+
var separatorArrayExplode = (style) => {
|
|
1324
|
+
switch (style) {
|
|
1325
|
+
case "label":
|
|
1326
|
+
return ".";
|
|
1327
|
+
case "matrix":
|
|
1328
|
+
return ";";
|
|
1329
|
+
case "simple":
|
|
1330
|
+
return ",";
|
|
1331
|
+
default:
|
|
1332
|
+
return "&";
|
|
1333
|
+
}
|
|
1334
|
+
};
|
|
1335
|
+
var separatorArrayNoExplode = (style) => {
|
|
1336
|
+
switch (style) {
|
|
1337
|
+
case "form":
|
|
1338
|
+
return ",";
|
|
1339
|
+
case "pipeDelimited":
|
|
1340
|
+
return "|";
|
|
1341
|
+
case "spaceDelimited":
|
|
1342
|
+
return "%20";
|
|
1343
|
+
default:
|
|
1344
|
+
return ",";
|
|
1345
|
+
}
|
|
1346
|
+
};
|
|
1347
|
+
var separatorObjectExplode = (style) => {
|
|
1348
|
+
switch (style) {
|
|
1349
|
+
case "label":
|
|
1350
|
+
return ".";
|
|
1351
|
+
case "matrix":
|
|
1352
|
+
return ";";
|
|
1353
|
+
case "simple":
|
|
1354
|
+
return ",";
|
|
1355
|
+
default:
|
|
1356
|
+
return "&";
|
|
1357
|
+
}
|
|
1358
|
+
};
|
|
1359
|
+
var serializeArrayParam = ({
|
|
1360
|
+
allowReserved,
|
|
1361
|
+
explode,
|
|
1362
|
+
name,
|
|
1363
|
+
style,
|
|
1364
|
+
value
|
|
1365
|
+
}) => {
|
|
1366
|
+
if (!explode) {
|
|
1367
|
+
const joinedValues2 = (allowReserved ? value : value.map((v) => encodeURIComponent(v))).join(separatorArrayNoExplode(style));
|
|
1368
|
+
switch (style) {
|
|
1369
|
+
case "label":
|
|
1370
|
+
return `.${joinedValues2}`;
|
|
1371
|
+
case "matrix":
|
|
1372
|
+
return `;${name}=${joinedValues2}`;
|
|
1373
|
+
case "simple":
|
|
1374
|
+
return joinedValues2;
|
|
1375
|
+
default:
|
|
1376
|
+
return `${name}=${joinedValues2}`;
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
const separator = separatorArrayExplode(style);
|
|
1380
|
+
const joinedValues = value.map((v) => {
|
|
1381
|
+
if (style === "label" || style === "simple") {
|
|
1382
|
+
return allowReserved ? v : encodeURIComponent(v);
|
|
1383
|
+
}
|
|
1384
|
+
return serializePrimitiveParam({
|
|
1385
|
+
allowReserved,
|
|
1386
|
+
name,
|
|
1387
|
+
value: v
|
|
1388
|
+
});
|
|
1389
|
+
}).join(separator);
|
|
1390
|
+
return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
|
|
1391
|
+
};
|
|
1392
|
+
var serializePrimitiveParam = ({
|
|
1393
|
+
allowReserved,
|
|
1394
|
+
name,
|
|
1395
|
+
value
|
|
1396
|
+
}) => {
|
|
1397
|
+
if (value === void 0 || value === null) {
|
|
1398
|
+
return "";
|
|
1399
|
+
}
|
|
1400
|
+
if (typeof value === "object") {
|
|
1401
|
+
throw new Error(
|
|
1402
|
+
"Deeply-nested arrays/objects aren\u2019t supported. Provide your own `querySerializer()` to handle these."
|
|
1403
|
+
);
|
|
1404
|
+
}
|
|
1405
|
+
return `${name}=${allowReserved ? value : encodeURIComponent(value)}`;
|
|
1406
|
+
};
|
|
1407
|
+
var serializeObjectParam = ({
|
|
1408
|
+
allowReserved,
|
|
1409
|
+
explode,
|
|
1410
|
+
name,
|
|
1411
|
+
style,
|
|
1412
|
+
value,
|
|
1413
|
+
valueOnly
|
|
1414
|
+
}) => {
|
|
1415
|
+
if (value instanceof Date) {
|
|
1416
|
+
return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`;
|
|
1417
|
+
}
|
|
1418
|
+
if (style !== "deepObject" && !explode) {
|
|
1419
|
+
let values = [];
|
|
1420
|
+
Object.entries(value).forEach(([key, v]) => {
|
|
1421
|
+
values = [
|
|
1422
|
+
...values,
|
|
1423
|
+
key,
|
|
1424
|
+
allowReserved ? v : encodeURIComponent(v)
|
|
1425
|
+
];
|
|
1426
|
+
});
|
|
1427
|
+
const joinedValues2 = values.join(",");
|
|
1428
|
+
switch (style) {
|
|
1429
|
+
case "form":
|
|
1430
|
+
return `${name}=${joinedValues2}`;
|
|
1431
|
+
case "label":
|
|
1432
|
+
return `.${joinedValues2}`;
|
|
1433
|
+
case "matrix":
|
|
1434
|
+
return `;${name}=${joinedValues2}`;
|
|
1435
|
+
default:
|
|
1436
|
+
return joinedValues2;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
const separator = separatorObjectExplode(style);
|
|
1440
|
+
const joinedValues = Object.entries(value).map(
|
|
1441
|
+
([key, v]) => serializePrimitiveParam({
|
|
1442
|
+
allowReserved,
|
|
1443
|
+
name: style === "deepObject" ? `${name}[${key}]` : key,
|
|
1444
|
+
value: v
|
|
1445
|
+
})
|
|
1446
|
+
).join(separator);
|
|
1447
|
+
return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
|
|
1448
|
+
};
|
|
1449
|
+
|
|
1450
|
+
// src/client/core/utils.gen.ts
|
|
1451
|
+
function getValidRequestBody(options) {
|
|
1452
|
+
const hasBody = options.body !== void 0;
|
|
1453
|
+
const isSerializedBody = hasBody && options.bodySerializer;
|
|
1454
|
+
if (isSerializedBody) {
|
|
1455
|
+
if ("serializedBody" in options) {
|
|
1456
|
+
const hasSerializedBody = options.serializedBody !== void 0 && options.serializedBody !== "";
|
|
1457
|
+
return hasSerializedBody ? options.serializedBody : null;
|
|
1458
|
+
}
|
|
1459
|
+
return options.body !== "" ? options.body : null;
|
|
1460
|
+
}
|
|
1461
|
+
if (hasBody) {
|
|
1462
|
+
return options.body;
|
|
1463
|
+
}
|
|
1464
|
+
return void 0;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
// src/client/core/auth.gen.ts
|
|
1468
|
+
var getAuthToken = async (auth, callback) => {
|
|
1469
|
+
const token = typeof callback === "function" ? await callback(auth) : callback;
|
|
1470
|
+
if (!token) {
|
|
1471
|
+
return;
|
|
1472
|
+
}
|
|
1473
|
+
if (auth.scheme === "bearer") {
|
|
1474
|
+
return `Bearer ${token}`;
|
|
1475
|
+
}
|
|
1476
|
+
if (auth.scheme === "basic") {
|
|
1477
|
+
return `Basic ${btoa(token)}`;
|
|
1478
|
+
}
|
|
1479
|
+
return token;
|
|
1480
|
+
};
|
|
1481
|
+
|
|
1482
|
+
// src/client/client/utils.gen.ts
|
|
1483
|
+
var PATH_PARAM_RE = /\{[^{}]+\}/g;
|
|
1484
|
+
var defaultPathSerializer = ({ path, url: _url }) => {
|
|
1485
|
+
let url = _url;
|
|
1486
|
+
const matches = _url.match(PATH_PARAM_RE);
|
|
1487
|
+
if (matches) {
|
|
1488
|
+
for (const match of matches) {
|
|
1489
|
+
let explode = false;
|
|
1490
|
+
let name = match.substring(1, match.length - 1);
|
|
1491
|
+
let style = "simple";
|
|
1492
|
+
if (name.endsWith("*")) {
|
|
1493
|
+
explode = true;
|
|
1494
|
+
name = name.substring(0, name.length - 1);
|
|
1495
|
+
}
|
|
1496
|
+
if (name.startsWith(".")) {
|
|
1497
|
+
name = name.substring(1);
|
|
1498
|
+
style = "label";
|
|
1499
|
+
} else if (name.startsWith(";")) {
|
|
1500
|
+
name = name.substring(1);
|
|
1501
|
+
style = "matrix";
|
|
1502
|
+
}
|
|
1503
|
+
const value = path[name];
|
|
1504
|
+
if (value === void 0 || value === null) {
|
|
1505
|
+
continue;
|
|
1506
|
+
}
|
|
1507
|
+
if (Array.isArray(value)) {
|
|
1508
|
+
url = url.replace(
|
|
1509
|
+
match,
|
|
1510
|
+
serializeArrayParam({ explode, name, style, value })
|
|
1511
|
+
);
|
|
1512
|
+
continue;
|
|
1513
|
+
}
|
|
1514
|
+
if (typeof value === "object") {
|
|
1515
|
+
url = url.replace(
|
|
1516
|
+
match,
|
|
1517
|
+
serializeObjectParam({
|
|
1518
|
+
explode,
|
|
1519
|
+
name,
|
|
1520
|
+
style,
|
|
1521
|
+
value,
|
|
1522
|
+
valueOnly: true
|
|
1523
|
+
})
|
|
1524
|
+
);
|
|
1525
|
+
continue;
|
|
1526
|
+
}
|
|
1527
|
+
if (style === "matrix") {
|
|
1528
|
+
url = url.replace(
|
|
1529
|
+
match,
|
|
1530
|
+
`;${serializePrimitiveParam({
|
|
1531
|
+
name,
|
|
1532
|
+
value
|
|
1533
|
+
})}`
|
|
1534
|
+
);
|
|
1535
|
+
continue;
|
|
1536
|
+
}
|
|
1537
|
+
const replaceValue = encodeURIComponent(
|
|
1538
|
+
style === "label" ? `.${value}` : value
|
|
1539
|
+
);
|
|
1540
|
+
url = url.replace(match, replaceValue);
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
return url;
|
|
1544
|
+
};
|
|
1545
|
+
var createQuerySerializer = ({
|
|
1546
|
+
parameters = {},
|
|
1547
|
+
...args
|
|
1548
|
+
} = {}) => {
|
|
1549
|
+
const querySerializer = (queryParams) => {
|
|
1550
|
+
const search = [];
|
|
1551
|
+
if (queryParams && typeof queryParams === "object") {
|
|
1552
|
+
for (const name in queryParams) {
|
|
1553
|
+
const value = queryParams[name];
|
|
1554
|
+
if (value === void 0 || value === null) {
|
|
1555
|
+
continue;
|
|
1556
|
+
}
|
|
1557
|
+
const options = parameters[name] || args;
|
|
1558
|
+
if (Array.isArray(value)) {
|
|
1559
|
+
const serializedArray = serializeArrayParam({
|
|
1560
|
+
allowReserved: options.allowReserved,
|
|
1561
|
+
explode: true,
|
|
1562
|
+
name,
|
|
1563
|
+
style: "form",
|
|
1564
|
+
value,
|
|
1565
|
+
...options.array
|
|
1566
|
+
});
|
|
1567
|
+
if (serializedArray) search.push(serializedArray);
|
|
1568
|
+
} else if (typeof value === "object") {
|
|
1569
|
+
const serializedObject = serializeObjectParam({
|
|
1570
|
+
allowReserved: options.allowReserved,
|
|
1571
|
+
explode: true,
|
|
1572
|
+
name,
|
|
1573
|
+
style: "deepObject",
|
|
1574
|
+
value,
|
|
1575
|
+
...options.object
|
|
1576
|
+
});
|
|
1577
|
+
if (serializedObject) search.push(serializedObject);
|
|
1578
|
+
} else {
|
|
1579
|
+
const serializedPrimitive = serializePrimitiveParam({
|
|
1580
|
+
allowReserved: options.allowReserved,
|
|
1581
|
+
name,
|
|
1582
|
+
value
|
|
1583
|
+
});
|
|
1584
|
+
if (serializedPrimitive) search.push(serializedPrimitive);
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
return search.join("&");
|
|
1589
|
+
};
|
|
1590
|
+
return querySerializer;
|
|
1591
|
+
};
|
|
1592
|
+
var getParseAs = (contentType) => {
|
|
1593
|
+
if (!contentType) {
|
|
1594
|
+
return "stream";
|
|
1595
|
+
}
|
|
1596
|
+
const cleanContent = contentType.split(";")[0]?.trim();
|
|
1597
|
+
if (!cleanContent) {
|
|
1598
|
+
return;
|
|
1599
|
+
}
|
|
1600
|
+
if (cleanContent.startsWith("application/json") || cleanContent.endsWith("+json")) {
|
|
1601
|
+
return "json";
|
|
1602
|
+
}
|
|
1603
|
+
if (cleanContent === "multipart/form-data") {
|
|
1604
|
+
return "formData";
|
|
1605
|
+
}
|
|
1606
|
+
if (["application/", "audio/", "image/", "video/"].some(
|
|
1607
|
+
(type) => cleanContent.startsWith(type)
|
|
1608
|
+
)) {
|
|
1609
|
+
return "blob";
|
|
1610
|
+
}
|
|
1611
|
+
if (cleanContent.startsWith("text/")) {
|
|
1612
|
+
return "text";
|
|
1613
|
+
}
|
|
1614
|
+
return;
|
|
1615
|
+
};
|
|
1616
|
+
var checkForExistence = (options, name) => {
|
|
1617
|
+
if (!name) {
|
|
1618
|
+
return false;
|
|
1619
|
+
}
|
|
1620
|
+
if (options.headers.has(name) || options.query?.[name] || options.headers.get("Cookie")?.includes(`${name}=`)) {
|
|
1621
|
+
return true;
|
|
1622
|
+
}
|
|
1623
|
+
return false;
|
|
1624
|
+
};
|
|
1625
|
+
var setAuthParams = async ({
|
|
1626
|
+
security,
|
|
1627
|
+
...options
|
|
1628
|
+
}) => {
|
|
1629
|
+
for (const auth of security) {
|
|
1630
|
+
if (checkForExistence(options, auth.name)) {
|
|
1631
|
+
continue;
|
|
1632
|
+
}
|
|
1633
|
+
const token = await getAuthToken(auth, options.auth);
|
|
1634
|
+
if (!token) {
|
|
1635
|
+
continue;
|
|
1636
|
+
}
|
|
1637
|
+
const name = auth.name ?? "Authorization";
|
|
1638
|
+
switch (auth.in) {
|
|
1639
|
+
case "query":
|
|
1640
|
+
if (!options.query) {
|
|
1641
|
+
options.query = {};
|
|
1642
|
+
}
|
|
1643
|
+
options.query[name] = token;
|
|
1644
|
+
break;
|
|
1645
|
+
case "cookie":
|
|
1646
|
+
options.headers.append("Cookie", `${name}=${token}`);
|
|
1647
|
+
break;
|
|
1648
|
+
case "header":
|
|
1649
|
+
default:
|
|
1650
|
+
options.headers.set(name, token);
|
|
1651
|
+
break;
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
};
|
|
1655
|
+
var buildUrl = (options) => {
|
|
1656
|
+
const url = getUrl({
|
|
1657
|
+
baseUrl: options.baseUrl,
|
|
1658
|
+
path: options.path,
|
|
1659
|
+
query: options.query,
|
|
1660
|
+
querySerializer: typeof options.querySerializer === "function" ? options.querySerializer : createQuerySerializer(options.querySerializer),
|
|
1661
|
+
url: options.url
|
|
1662
|
+
});
|
|
1663
|
+
return url;
|
|
1664
|
+
};
|
|
1665
|
+
var getUrl = ({
|
|
1666
|
+
baseUrl,
|
|
1667
|
+
path,
|
|
1668
|
+
query,
|
|
1669
|
+
querySerializer,
|
|
1670
|
+
url: _url
|
|
1671
|
+
}) => {
|
|
1672
|
+
const pathUrl = _url.startsWith("/") ? _url : `/${_url}`;
|
|
1673
|
+
let url = (baseUrl ?? "") + pathUrl;
|
|
1674
|
+
if (path) {
|
|
1675
|
+
url = defaultPathSerializer({ path, url });
|
|
1676
|
+
}
|
|
1677
|
+
let search = query ? querySerializer(query) : "";
|
|
1678
|
+
if (search.startsWith("?")) {
|
|
1679
|
+
search = search.substring(1);
|
|
1680
|
+
}
|
|
1681
|
+
if (search) {
|
|
1682
|
+
url += `?${search}`;
|
|
1683
|
+
}
|
|
1684
|
+
return url;
|
|
1685
|
+
};
|
|
1686
|
+
var mergeConfigs = (a, b) => {
|
|
1687
|
+
const config = { ...a, ...b };
|
|
1688
|
+
if (config.baseUrl?.endsWith("/")) {
|
|
1689
|
+
config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1);
|
|
1690
|
+
}
|
|
1691
|
+
config.headers = mergeHeaders(a.headers, b.headers);
|
|
1692
|
+
return config;
|
|
1693
|
+
};
|
|
1694
|
+
var headersEntries = (headers) => {
|
|
1695
|
+
const entries = [];
|
|
1696
|
+
headers.forEach((value, key) => {
|
|
1697
|
+
entries.push([key, value]);
|
|
1698
|
+
});
|
|
1699
|
+
return entries;
|
|
1700
|
+
};
|
|
1701
|
+
var mergeHeaders = (...headers) => {
|
|
1702
|
+
const mergedHeaders = new Headers();
|
|
1703
|
+
for (const header of headers) {
|
|
1704
|
+
if (!header || typeof header !== "object") {
|
|
1705
|
+
continue;
|
|
1706
|
+
}
|
|
1707
|
+
const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header);
|
|
1708
|
+
for (const [key, value] of iterator) {
|
|
1709
|
+
if (value === null) {
|
|
1710
|
+
mergedHeaders.delete(key);
|
|
1711
|
+
} else if (Array.isArray(value)) {
|
|
1712
|
+
for (const v of value) {
|
|
1713
|
+
mergedHeaders.append(key, v);
|
|
1714
|
+
}
|
|
1715
|
+
} else if (value !== void 0) {
|
|
1716
|
+
mergedHeaders.set(
|
|
1717
|
+
key,
|
|
1718
|
+
typeof value === "object" ? JSON.stringify(value) : value
|
|
1719
|
+
);
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
return mergedHeaders;
|
|
1724
|
+
};
|
|
1725
|
+
var Interceptors = class {
|
|
1726
|
+
constructor() {
|
|
1727
|
+
this.fns = [];
|
|
1728
|
+
}
|
|
1729
|
+
clear() {
|
|
1730
|
+
this.fns = [];
|
|
1731
|
+
}
|
|
1732
|
+
eject(id) {
|
|
1733
|
+
const index = this.getInterceptorIndex(id);
|
|
1734
|
+
if (this.fns[index]) {
|
|
1735
|
+
this.fns[index] = null;
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
exists(id) {
|
|
1739
|
+
const index = this.getInterceptorIndex(id);
|
|
1740
|
+
return Boolean(this.fns[index]);
|
|
1741
|
+
}
|
|
1742
|
+
getInterceptorIndex(id) {
|
|
1743
|
+
if (typeof id === "number") {
|
|
1744
|
+
return this.fns[id] ? id : -1;
|
|
1745
|
+
}
|
|
1746
|
+
return this.fns.indexOf(id);
|
|
1747
|
+
}
|
|
1748
|
+
update(id, fn) {
|
|
1749
|
+
const index = this.getInterceptorIndex(id);
|
|
1750
|
+
if (this.fns[index]) {
|
|
1751
|
+
this.fns[index] = fn;
|
|
1752
|
+
return id;
|
|
1753
|
+
}
|
|
1754
|
+
return false;
|
|
1755
|
+
}
|
|
1756
|
+
use(fn) {
|
|
1757
|
+
this.fns.push(fn);
|
|
1758
|
+
return this.fns.length - 1;
|
|
1759
|
+
}
|
|
1760
|
+
};
|
|
1761
|
+
var createInterceptors = () => ({
|
|
1762
|
+
error: new Interceptors(),
|
|
1763
|
+
request: new Interceptors(),
|
|
1764
|
+
response: new Interceptors()
|
|
1765
|
+
});
|
|
1766
|
+
var defaultQuerySerializer = createQuerySerializer({
|
|
1767
|
+
allowReserved: false,
|
|
1768
|
+
array: {
|
|
1769
|
+
explode: true,
|
|
1770
|
+
style: "form"
|
|
1771
|
+
},
|
|
1772
|
+
object: {
|
|
1773
|
+
explode: true,
|
|
1774
|
+
style: "deepObject"
|
|
1775
|
+
}
|
|
1776
|
+
});
|
|
1777
|
+
var defaultHeaders = {
|
|
1778
|
+
"Content-Type": "application/json"
|
|
1779
|
+
};
|
|
1780
|
+
var createConfig = (override = {}) => ({
|
|
1781
|
+
...jsonBodySerializer,
|
|
1782
|
+
headers: defaultHeaders,
|
|
1783
|
+
parseAs: "auto",
|
|
1784
|
+
querySerializer: defaultQuerySerializer,
|
|
1785
|
+
...override
|
|
1786
|
+
});
|
|
1787
|
+
|
|
1788
|
+
// src/client/client/client.gen.ts
|
|
1789
|
+
var createClient = (config = {}) => {
|
|
1790
|
+
let _config = mergeConfigs(createConfig(), config);
|
|
1791
|
+
const getConfig = () => ({ ..._config });
|
|
1792
|
+
const setConfig = (config2) => {
|
|
1793
|
+
_config = mergeConfigs(_config, config2);
|
|
1794
|
+
return getConfig();
|
|
1795
|
+
};
|
|
1796
|
+
const interceptors = createInterceptors();
|
|
1797
|
+
const beforeRequest = async (options) => {
|
|
1798
|
+
const opts = {
|
|
1799
|
+
..._config,
|
|
1800
|
+
...options,
|
|
1801
|
+
fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
|
|
1802
|
+
headers: mergeHeaders(_config.headers, options.headers),
|
|
1803
|
+
serializedBody: void 0
|
|
1804
|
+
};
|
|
1805
|
+
if (opts.security) {
|
|
1806
|
+
await setAuthParams({
|
|
1807
|
+
...opts,
|
|
1808
|
+
security: opts.security
|
|
1809
|
+
});
|
|
1810
|
+
}
|
|
1811
|
+
if (opts.requestValidator) {
|
|
1812
|
+
await opts.requestValidator(opts);
|
|
1813
|
+
}
|
|
1814
|
+
if (opts.body !== void 0 && opts.bodySerializer) {
|
|
1815
|
+
opts.serializedBody = opts.bodySerializer(opts.body);
|
|
1816
|
+
}
|
|
1817
|
+
if (opts.body === void 0 || opts.serializedBody === "") {
|
|
1818
|
+
opts.headers.delete("Content-Type");
|
|
1819
|
+
}
|
|
1820
|
+
const url = buildUrl(opts);
|
|
1821
|
+
return { opts, url };
|
|
1822
|
+
};
|
|
1823
|
+
const request = async (options) => {
|
|
1824
|
+
const { opts, url } = await beforeRequest(options);
|
|
1825
|
+
for (const fn of interceptors.request.fns) {
|
|
1826
|
+
if (fn) {
|
|
1827
|
+
await fn(opts);
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
const _fetch = opts.fetch;
|
|
1831
|
+
const requestInit = {
|
|
1832
|
+
...opts,
|
|
1833
|
+
body: getValidRequestBody(opts)
|
|
1834
|
+
};
|
|
1835
|
+
let response = await _fetch(url, requestInit);
|
|
1836
|
+
for (const fn of interceptors.response.fns) {
|
|
1837
|
+
if (fn) {
|
|
1838
|
+
response = await fn(response, opts);
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
const result = {
|
|
1842
|
+
response
|
|
1843
|
+
};
|
|
1844
|
+
if (response.ok) {
|
|
1845
|
+
const parseAs = (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json";
|
|
1846
|
+
if (response.status === 204 || response.headers.get("Content-Length") === "0") {
|
|
1847
|
+
let emptyData;
|
|
1848
|
+
switch (parseAs) {
|
|
1849
|
+
case "arrayBuffer":
|
|
1850
|
+
case "blob":
|
|
1851
|
+
case "text":
|
|
1852
|
+
emptyData = await response[parseAs]();
|
|
1853
|
+
break;
|
|
1854
|
+
case "formData":
|
|
1855
|
+
emptyData = new FormData();
|
|
1856
|
+
break;
|
|
1857
|
+
case "stream":
|
|
1858
|
+
emptyData = response.body;
|
|
1859
|
+
break;
|
|
1860
|
+
case "json":
|
|
1861
|
+
default:
|
|
1862
|
+
emptyData = {};
|
|
1863
|
+
break;
|
|
1864
|
+
}
|
|
1865
|
+
return {
|
|
1866
|
+
data: emptyData,
|
|
1867
|
+
...result
|
|
1868
|
+
};
|
|
1869
|
+
}
|
|
1870
|
+
let data;
|
|
1871
|
+
switch (parseAs) {
|
|
1872
|
+
case "arrayBuffer":
|
|
1873
|
+
case "blob":
|
|
1874
|
+
case "formData":
|
|
1875
|
+
case "json":
|
|
1876
|
+
case "text":
|
|
1877
|
+
data = await response[parseAs]();
|
|
1878
|
+
break;
|
|
1879
|
+
case "stream":
|
|
1880
|
+
return {
|
|
1881
|
+
data: response.body,
|
|
1882
|
+
...result
|
|
1883
|
+
};
|
|
1884
|
+
}
|
|
1885
|
+
if (parseAs === "json") {
|
|
1886
|
+
if (opts.responseValidator) {
|
|
1887
|
+
await opts.responseValidator(data);
|
|
1888
|
+
}
|
|
1889
|
+
if (opts.responseTransformer) {
|
|
1890
|
+
data = await opts.responseTransformer(data);
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
return {
|
|
1894
|
+
data,
|
|
1895
|
+
...result
|
|
1896
|
+
};
|
|
1897
|
+
}
|
|
1898
|
+
const textError = await response.text();
|
|
1899
|
+
let jsonError;
|
|
1900
|
+
try {
|
|
1901
|
+
jsonError = JSON.parse(textError);
|
|
1902
|
+
} catch {
|
|
1903
|
+
}
|
|
1904
|
+
const error = jsonError ?? textError;
|
|
1905
|
+
let finalError = error;
|
|
1906
|
+
for (const fn of interceptors.error.fns) {
|
|
1907
|
+
if (fn) {
|
|
1908
|
+
finalError = await fn(error, response, opts);
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
finalError = finalError || {};
|
|
1912
|
+
if (opts.throwOnError) {
|
|
1913
|
+
throw finalError;
|
|
1914
|
+
}
|
|
1915
|
+
return {
|
|
1916
|
+
error: finalError,
|
|
1917
|
+
...result
|
|
1918
|
+
};
|
|
1919
|
+
};
|
|
1920
|
+
const makeMethodFn = (method) => (options) => request({ ...options, method });
|
|
1921
|
+
const makeSseFn = (method) => async (options) => {
|
|
1922
|
+
const { opts, url } = await beforeRequest(options);
|
|
1923
|
+
return createSseClient({
|
|
1924
|
+
...opts,
|
|
1925
|
+
body: opts.body,
|
|
1926
|
+
headers: opts.headers,
|
|
1927
|
+
method,
|
|
1928
|
+
onRequest: async (url2, init) => {
|
|
1929
|
+
let request2 = new Request(url2, init);
|
|
1930
|
+
const requestInit = {
|
|
1931
|
+
...init,
|
|
1932
|
+
method: init.method,
|
|
1933
|
+
url: url2
|
|
1934
|
+
};
|
|
1935
|
+
for (const fn of interceptors.request.fns) {
|
|
1936
|
+
if (fn) {
|
|
1937
|
+
await fn(requestInit);
|
|
1938
|
+
request2 = new Request(requestInit.url, requestInit);
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
return request2;
|
|
1942
|
+
},
|
|
1943
|
+
url
|
|
1944
|
+
});
|
|
1945
|
+
};
|
|
1946
|
+
return {
|
|
1947
|
+
buildUrl,
|
|
1948
|
+
connect: makeMethodFn("CONNECT"),
|
|
1949
|
+
delete: makeMethodFn("DELETE"),
|
|
1950
|
+
get: makeMethodFn("GET"),
|
|
1951
|
+
getConfig,
|
|
1952
|
+
head: makeMethodFn("HEAD"),
|
|
1953
|
+
interceptors,
|
|
1954
|
+
options: makeMethodFn("OPTIONS"),
|
|
1955
|
+
patch: makeMethodFn("PATCH"),
|
|
1956
|
+
post: makeMethodFn("POST"),
|
|
1957
|
+
put: makeMethodFn("PUT"),
|
|
1958
|
+
request,
|
|
1959
|
+
setConfig,
|
|
1960
|
+
sse: {
|
|
1961
|
+
connect: makeSseFn("CONNECT"),
|
|
1962
|
+
delete: makeSseFn("DELETE"),
|
|
1963
|
+
get: makeSseFn("GET"),
|
|
1964
|
+
head: makeSseFn("HEAD"),
|
|
1965
|
+
options: makeSseFn("OPTIONS"),
|
|
1966
|
+
patch: makeSseFn("PATCH"),
|
|
1967
|
+
post: makeSseFn("POST"),
|
|
1968
|
+
put: makeSseFn("PUT"),
|
|
1969
|
+
trace: makeSseFn("TRACE")
|
|
1970
|
+
},
|
|
1971
|
+
trace: makeMethodFn("TRACE")
|
|
1972
|
+
};
|
|
1973
|
+
};
|
|
1974
|
+
|
|
1975
|
+
// src/client/client.gen.ts
|
|
1976
|
+
var client = createClient(createClientConfig(createConfig()));
|
|
1977
|
+
|
|
1978
|
+
// src/client/sdk.gen.ts
|
|
1979
|
+
var postApiV1ImagesGenerations = (options) => {
|
|
1980
|
+
return (options.client ?? client).post({
|
|
1981
|
+
url: "/api/v1/images/generations",
|
|
1982
|
+
...options,
|
|
1983
|
+
headers: {
|
|
1984
|
+
"Content-Type": "application/json",
|
|
1985
|
+
...options.headers
|
|
1986
|
+
}
|
|
1987
|
+
});
|
|
1988
|
+
};
|
|
1989
|
+
var getApiV1Models = (options) => {
|
|
1990
|
+
return (options?.client ?? client).get({
|
|
1991
|
+
url: "/api/v1/models",
|
|
1992
|
+
...options
|
|
1993
|
+
});
|
|
1994
|
+
};
|
|
1995
|
+
|
|
1996
|
+
// src/react/useImageGeneration.ts
|
|
1997
|
+
function useImageGeneration(options = {}) {
|
|
1998
|
+
const { getToken, baseUrl = BASE_URL, onFinish, onError } = options;
|
|
1999
|
+
const [isLoading, setIsLoading] = useState3(false);
|
|
2000
|
+
const abortControllerRef = useRef2(null);
|
|
2001
|
+
useEffect2(() => {
|
|
2002
|
+
return () => {
|
|
2003
|
+
if (abortControllerRef.current) {
|
|
2004
|
+
abortControllerRef.current.abort();
|
|
2005
|
+
abortControllerRef.current = null;
|
|
2006
|
+
}
|
|
2007
|
+
};
|
|
2008
|
+
}, []);
|
|
2009
|
+
const stop = useCallback3(() => {
|
|
2010
|
+
if (abortControllerRef.current) {
|
|
2011
|
+
abortControllerRef.current.abort();
|
|
2012
|
+
abortControllerRef.current = null;
|
|
2013
|
+
}
|
|
2014
|
+
}, []);
|
|
2015
|
+
const generateImage = useCallback3(
|
|
2016
|
+
async (args) => {
|
|
2017
|
+
if (abortControllerRef.current) {
|
|
2018
|
+
abortControllerRef.current.abort();
|
|
2019
|
+
}
|
|
2020
|
+
const abortController = new AbortController();
|
|
2021
|
+
abortControllerRef.current = abortController;
|
|
2022
|
+
setIsLoading(true);
|
|
2023
|
+
try {
|
|
2024
|
+
if (!getToken) {
|
|
2025
|
+
throw new Error("Token getter function is required.");
|
|
2026
|
+
}
|
|
2027
|
+
const token = await getToken();
|
|
2028
|
+
if (!token) {
|
|
2029
|
+
throw new Error("No access token available.");
|
|
2030
|
+
}
|
|
2031
|
+
const response = await postApiV1ImagesGenerations({
|
|
2032
|
+
baseUrl,
|
|
2033
|
+
body: args,
|
|
2034
|
+
headers: {
|
|
2035
|
+
Authorization: `Bearer ${token}`
|
|
2036
|
+
},
|
|
2037
|
+
signal: abortController.signal
|
|
2038
|
+
});
|
|
2039
|
+
if (response.error) {
|
|
2040
|
+
const errorMsg = response.error.error || "Failed to generate image";
|
|
2041
|
+
throw new Error(errorMsg);
|
|
2042
|
+
}
|
|
2043
|
+
if (!response.data) {
|
|
2044
|
+
throw new Error("No data received from image generation API");
|
|
2045
|
+
}
|
|
2046
|
+
const result = response.data;
|
|
2047
|
+
if (onFinish) {
|
|
2048
|
+
onFinish(result);
|
|
2049
|
+
}
|
|
2050
|
+
return { data: result, error: null };
|
|
2051
|
+
} catch (err) {
|
|
2052
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
2053
|
+
return { data: null, error: "Request aborted" };
|
|
2054
|
+
}
|
|
2055
|
+
const errorMsg = err instanceof Error ? err.message : "Failed to generate image.";
|
|
2056
|
+
const errorObj = err instanceof Error ? err : new Error(errorMsg);
|
|
2057
|
+
if (onError) {
|
|
2058
|
+
onError(errorObj);
|
|
2059
|
+
}
|
|
2060
|
+
return { data: null, error: errorMsg };
|
|
2061
|
+
} finally {
|
|
2062
|
+
if (abortControllerRef.current === abortController) {
|
|
2063
|
+
setIsLoading(false);
|
|
2064
|
+
abortControllerRef.current = null;
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
},
|
|
2068
|
+
[getToken, baseUrl, onFinish, onError]
|
|
2069
|
+
);
|
|
2070
|
+
return {
|
|
2071
|
+
isLoading,
|
|
2072
|
+
generateImage,
|
|
2073
|
+
stop
|
|
2074
|
+
};
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
// src/react/useModels.ts
|
|
2078
|
+
import { useCallback as useCallback4, useEffect as useEffect3, useRef as useRef3, useState as useState4 } from "react";
|
|
2079
|
+
function useModels(options = {}) {
|
|
2080
|
+
const { getToken, baseUrl = BASE_URL, provider, autoFetch = true } = options;
|
|
2081
|
+
const [models, setModels] = useState4([]);
|
|
2082
|
+
const [isLoading, setIsLoading] = useState4(false);
|
|
2083
|
+
const [error, setError] = useState4(null);
|
|
2084
|
+
const getTokenRef = useRef3(getToken);
|
|
2085
|
+
const baseUrlRef = useRef3(baseUrl);
|
|
2086
|
+
const providerRef = useRef3(provider);
|
|
2087
|
+
const abortControllerRef = useRef3(null);
|
|
2088
|
+
useEffect3(() => {
|
|
2089
|
+
getTokenRef.current = getToken;
|
|
2090
|
+
baseUrlRef.current = baseUrl;
|
|
2091
|
+
providerRef.current = provider;
|
|
2092
|
+
});
|
|
2093
|
+
useEffect3(() => {
|
|
2094
|
+
return () => {
|
|
2095
|
+
if (abortControllerRef.current) {
|
|
2096
|
+
abortControllerRef.current.abort();
|
|
2097
|
+
abortControllerRef.current = null;
|
|
2098
|
+
}
|
|
2099
|
+
};
|
|
2100
|
+
}, []);
|
|
2101
|
+
const fetchModels = useCallback4(async () => {
|
|
2102
|
+
if (abortControllerRef.current) {
|
|
2103
|
+
abortControllerRef.current.abort();
|
|
2104
|
+
}
|
|
2105
|
+
const abortController = new AbortController();
|
|
2106
|
+
abortControllerRef.current = abortController;
|
|
2107
|
+
const signal = abortController.signal;
|
|
2108
|
+
setIsLoading(true);
|
|
2109
|
+
setError(null);
|
|
2110
|
+
try {
|
|
2111
|
+
let token;
|
|
2112
|
+
if (getTokenRef.current) {
|
|
2113
|
+
token = await getTokenRef.current() ?? void 0;
|
|
2114
|
+
}
|
|
2115
|
+
if (signal.aborted) return;
|
|
2116
|
+
const headers = {};
|
|
2117
|
+
if (token) {
|
|
2118
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
2119
|
+
}
|
|
2120
|
+
let allModels = [];
|
|
2121
|
+
let nextPageToken;
|
|
2122
|
+
do {
|
|
2123
|
+
if (signal.aborted) return;
|
|
2124
|
+
const response = await getApiV1Models({
|
|
2125
|
+
baseUrl: baseUrlRef.current,
|
|
2126
|
+
headers,
|
|
2127
|
+
query: {
|
|
2128
|
+
provider: providerRef.current,
|
|
2129
|
+
page_token: nextPageToken
|
|
2130
|
+
},
|
|
2131
|
+
signal
|
|
2132
|
+
});
|
|
2133
|
+
if (response.error) {
|
|
2134
|
+
const errorMsg = response.error.error ?? "Failed to fetch models";
|
|
2135
|
+
throw new Error(errorMsg);
|
|
2136
|
+
}
|
|
2137
|
+
if (response.data) {
|
|
2138
|
+
const newModels = response.data.data || [];
|
|
2139
|
+
allModels = [...allModels, ...newModels];
|
|
2140
|
+
nextPageToken = response.data.next_page_token;
|
|
2141
|
+
}
|
|
2142
|
+
} while (nextPageToken);
|
|
2143
|
+
if (signal.aborted) return;
|
|
2144
|
+
setModels(allModels);
|
|
2145
|
+
} catch (err) {
|
|
2146
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
2147
|
+
return;
|
|
2148
|
+
}
|
|
2149
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
2150
|
+
} finally {
|
|
2151
|
+
if (!signal.aborted) {
|
|
2152
|
+
setIsLoading(false);
|
|
2153
|
+
}
|
|
2154
|
+
if (abortControllerRef.current === abortController) {
|
|
2155
|
+
abortControllerRef.current = null;
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
}, []);
|
|
2159
|
+
const refetch = useCallback4(async () => {
|
|
2160
|
+
setModels([]);
|
|
2161
|
+
await fetchModels();
|
|
2162
|
+
}, [fetchModels]);
|
|
2163
|
+
const hasFetchedRef = useRef3(false);
|
|
2164
|
+
useEffect3(() => {
|
|
2165
|
+
if (autoFetch && !hasFetchedRef.current) {
|
|
2166
|
+
hasFetchedRef.current = true;
|
|
2167
|
+
fetchModels();
|
|
2168
|
+
}
|
|
2169
|
+
if (!autoFetch) {
|
|
2170
|
+
hasFetchedRef.current = false;
|
|
2171
|
+
}
|
|
2172
|
+
}, [autoFetch, fetchModels]);
|
|
2173
|
+
return {
|
|
2174
|
+
models,
|
|
2175
|
+
isLoading,
|
|
2176
|
+
error,
|
|
2177
|
+
refetch
|
|
2178
|
+
};
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
// src/expo/useMemoryStorage.ts
|
|
2182
|
+
import { useCallback as useCallback5, useState as useState5, useMemo as useMemo2, useRef as useRef4 } from "react";
|
|
2183
|
+
import { postApiV1ChatCompletions } from "@reverbia/sdk";
|
|
2184
|
+
|
|
2185
|
+
// src/lib/db/memory/schema.ts
|
|
2186
|
+
import { appSchema as appSchema2, tableSchema as tableSchema2 } from "@nozbe/watermelondb";
|
|
2187
|
+
var memoryStorageSchema = appSchema2({
|
|
2188
|
+
version: 1,
|
|
2189
|
+
tables: [
|
|
2190
|
+
tableSchema2({
|
|
2191
|
+
name: "memories",
|
|
2192
|
+
columns: [
|
|
2193
|
+
{ name: "type", type: "string", isIndexed: true },
|
|
2194
|
+
{ name: "namespace", type: "string", isIndexed: true },
|
|
2195
|
+
{ name: "key", type: "string", isIndexed: true },
|
|
2196
|
+
{ name: "value", type: "string" },
|
|
2197
|
+
{ name: "raw_evidence", type: "string" },
|
|
2198
|
+
{ name: "confidence", type: "number" },
|
|
2199
|
+
{ name: "pii", type: "boolean", isIndexed: true },
|
|
2200
|
+
{ name: "composite_key", type: "string", isIndexed: true },
|
|
2201
|
+
{ name: "unique_key", type: "string", isIndexed: true },
|
|
2202
|
+
{ name: "created_at", type: "number", isIndexed: true },
|
|
2203
|
+
{ name: "updated_at", type: "number" },
|
|
2204
|
+
{ name: "embedding", type: "string", isOptional: true },
|
|
2205
|
+
{ name: "embedding_model", type: "string", isOptional: true },
|
|
2206
|
+
{ name: "is_deleted", type: "boolean", isIndexed: true }
|
|
2207
|
+
]
|
|
2208
|
+
})
|
|
2209
|
+
]
|
|
2210
|
+
});
|
|
2211
|
+
|
|
2212
|
+
// src/lib/db/memory/models.ts
|
|
2213
|
+
import { Model as Model2 } from "@nozbe/watermelondb";
|
|
2214
|
+
import { field as field2, text as text2, date as date2, json as json2 } from "@nozbe/watermelondb/decorators";
|
|
2215
|
+
var Memory = class extends Model2 {
|
|
2216
|
+
};
|
|
2217
|
+
Memory.table = "memories";
|
|
2218
|
+
__decorateClass([
|
|
2219
|
+
text2("type")
|
|
2220
|
+
], Memory.prototype, "type", 2);
|
|
2221
|
+
__decorateClass([
|
|
2222
|
+
text2("namespace")
|
|
2223
|
+
], Memory.prototype, "namespace", 2);
|
|
2224
|
+
__decorateClass([
|
|
2225
|
+
text2("key")
|
|
2226
|
+
], Memory.prototype, "key", 2);
|
|
2227
|
+
__decorateClass([
|
|
2228
|
+
text2("value")
|
|
2229
|
+
], Memory.prototype, "value", 2);
|
|
2230
|
+
__decorateClass([
|
|
2231
|
+
text2("raw_evidence")
|
|
2232
|
+
], Memory.prototype, "rawEvidence", 2);
|
|
2233
|
+
__decorateClass([
|
|
2234
|
+
field2("confidence")
|
|
2235
|
+
], Memory.prototype, "confidence", 2);
|
|
2236
|
+
__decorateClass([
|
|
2237
|
+
field2("pii")
|
|
2238
|
+
], Memory.prototype, "pii", 2);
|
|
2239
|
+
__decorateClass([
|
|
2240
|
+
text2("composite_key")
|
|
2241
|
+
], Memory.prototype, "compositeKey", 2);
|
|
2242
|
+
__decorateClass([
|
|
2243
|
+
text2("unique_key")
|
|
2244
|
+
], Memory.prototype, "uniqueKey", 2);
|
|
2245
|
+
__decorateClass([
|
|
2246
|
+
date2("created_at")
|
|
2247
|
+
], Memory.prototype, "createdAt", 2);
|
|
2248
|
+
__decorateClass([
|
|
2249
|
+
date2("updated_at")
|
|
2250
|
+
], Memory.prototype, "updatedAt", 2);
|
|
2251
|
+
__decorateClass([
|
|
2252
|
+
json2("embedding", (json3) => json3)
|
|
2253
|
+
], Memory.prototype, "embedding", 2);
|
|
2254
|
+
__decorateClass([
|
|
2255
|
+
text2("embedding_model")
|
|
2256
|
+
], Memory.prototype, "embeddingModel", 2);
|
|
2257
|
+
__decorateClass([
|
|
2258
|
+
field2("is_deleted")
|
|
2259
|
+
], Memory.prototype, "isDeleted", 2);
|
|
2260
|
+
|
|
2261
|
+
// src/lib/db/memory/types.ts
|
|
2262
|
+
function generateCompositeKey(namespace, key) {
|
|
2263
|
+
return `${namespace}:${key}`;
|
|
2264
|
+
}
|
|
2265
|
+
function generateUniqueKey(namespace, key, value) {
|
|
2266
|
+
return `${namespace}:${key}:${value}`;
|
|
2267
|
+
}
|
|
2268
|
+
function cosineSimilarity(a, b) {
|
|
2269
|
+
if (a.length !== b.length) {
|
|
2270
|
+
throw new Error("Vectors must have the same length");
|
|
2271
|
+
}
|
|
2272
|
+
let dotProduct = 0;
|
|
2273
|
+
let normA = 0;
|
|
2274
|
+
let normB = 0;
|
|
2275
|
+
for (let i = 0; i < a.length; i++) {
|
|
2276
|
+
dotProduct += a[i] * b[i];
|
|
2277
|
+
normA += a[i] * a[i];
|
|
2278
|
+
normB += b[i] * b[i];
|
|
2279
|
+
}
|
|
2280
|
+
const denominator = Math.sqrt(normA) * Math.sqrt(normB);
|
|
2281
|
+
if (denominator === 0) {
|
|
2282
|
+
return 0;
|
|
2283
|
+
}
|
|
2284
|
+
return dotProduct / denominator;
|
|
2285
|
+
}
|
|
2286
|
+
|
|
2287
|
+
// src/lib/db/memory/operations.ts
|
|
2288
|
+
import { Q as Q2 } from "@nozbe/watermelondb";
|
|
2289
|
+
function memoryToStored(memory) {
|
|
2290
|
+
return {
|
|
2291
|
+
uniqueId: memory.id,
|
|
2292
|
+
type: memory.type,
|
|
2293
|
+
namespace: memory.namespace,
|
|
2294
|
+
key: memory.key,
|
|
2295
|
+
value: memory.value,
|
|
2296
|
+
rawEvidence: memory.rawEvidence,
|
|
2297
|
+
confidence: memory.confidence,
|
|
2298
|
+
pii: memory.pii,
|
|
2299
|
+
compositeKey: memory.compositeKey,
|
|
2300
|
+
uniqueKey: memory.uniqueKey,
|
|
2301
|
+
createdAt: memory.createdAt,
|
|
2302
|
+
updatedAt: memory.updatedAt,
|
|
2303
|
+
embedding: memory.embedding,
|
|
2304
|
+
embeddingModel: memory.embeddingModel,
|
|
2305
|
+
isDeleted: memory.isDeleted
|
|
2306
|
+
};
|
|
2307
|
+
}
|
|
2308
|
+
async function getAllMemoriesOp(ctx) {
|
|
2309
|
+
const results = await ctx.memoriesCollection.query(Q2.where("is_deleted", false), Q2.sortBy("created_at", Q2.desc)).fetch();
|
|
2310
|
+
return results.map(memoryToStored);
|
|
2311
|
+
}
|
|
2312
|
+
async function getMemoryByIdOp(ctx, id) {
|
|
2313
|
+
try {
|
|
2314
|
+
const memory = await ctx.memoriesCollection.find(id);
|
|
2315
|
+
if (memory.isDeleted) return null;
|
|
2316
|
+
return memoryToStored(memory);
|
|
2317
|
+
} catch {
|
|
2318
|
+
return null;
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
async function getMemoriesByNamespaceOp(ctx, namespace) {
|
|
2322
|
+
const results = await ctx.memoriesCollection.query(
|
|
2323
|
+
Q2.where("namespace", namespace),
|
|
2324
|
+
Q2.where("is_deleted", false),
|
|
2325
|
+
Q2.sortBy("created_at", Q2.desc)
|
|
2326
|
+
).fetch();
|
|
2327
|
+
return results.map(memoryToStored);
|
|
2328
|
+
}
|
|
2329
|
+
async function getMemoriesByKeyOp(ctx, namespace, key) {
|
|
2330
|
+
const compositeKey = generateCompositeKey(namespace, key);
|
|
2331
|
+
const results = await ctx.memoriesCollection.query(
|
|
2332
|
+
Q2.where("composite_key", compositeKey),
|
|
2333
|
+
Q2.where("is_deleted", false),
|
|
2334
|
+
Q2.sortBy("created_at", Q2.desc)
|
|
2335
|
+
).fetch();
|
|
2336
|
+
return results.map(memoryToStored);
|
|
2337
|
+
}
|
|
2338
|
+
async function saveMemoryOp(ctx, opts) {
|
|
2339
|
+
if (!opts.namespace || typeof opts.namespace !== "string") {
|
|
2340
|
+
throw new Error("Namespace is required and must be a string");
|
|
2341
|
+
}
|
|
2342
|
+
if (!opts.key || typeof opts.key !== "string") {
|
|
2343
|
+
throw new Error("Key is required and must be a string");
|
|
2344
|
+
}
|
|
2345
|
+
if (!opts.value || typeof opts.value !== "string") {
|
|
2346
|
+
throw new Error("Value is required and must be a string");
|
|
2347
|
+
}
|
|
2348
|
+
if (!opts.type || typeof opts.type !== "string") {
|
|
2349
|
+
throw new Error("Type is required and must be a string");
|
|
2350
|
+
}
|
|
2351
|
+
if (typeof opts.confidence !== "number" || opts.confidence < 0 || opts.confidence > 1) {
|
|
2352
|
+
throw new Error("Confidence must be a number between 0 and 1");
|
|
2353
|
+
}
|
|
2354
|
+
if (typeof opts.pii !== "boolean") {
|
|
2355
|
+
throw new Error("PII must be a boolean");
|
|
2356
|
+
}
|
|
2357
|
+
const compositeKey = generateCompositeKey(opts.namespace, opts.key);
|
|
2358
|
+
const uniqueKey = generateUniqueKey(opts.namespace, opts.key, opts.value);
|
|
2359
|
+
const result = await ctx.database.write(async () => {
|
|
2360
|
+
const existing = await ctx.memoriesCollection.query(Q2.where("unique_key", uniqueKey)).fetch();
|
|
2361
|
+
if (existing.length > 0) {
|
|
2362
|
+
const existingMemory = existing[0];
|
|
2363
|
+
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;
|
|
2364
|
+
await existingMemory.update((mem) => {
|
|
2365
|
+
mem._setRaw("type", opts.type);
|
|
2366
|
+
mem._setRaw("namespace", opts.namespace);
|
|
2367
|
+
mem._setRaw("key", opts.key);
|
|
2368
|
+
mem._setRaw("value", opts.value);
|
|
2369
|
+
mem._setRaw("raw_evidence", opts.rawEvidence);
|
|
2370
|
+
mem._setRaw("confidence", opts.confidence);
|
|
2371
|
+
mem._setRaw("pii", opts.pii);
|
|
2372
|
+
mem._setRaw("composite_key", compositeKey);
|
|
2373
|
+
mem._setRaw("unique_key", uniqueKey);
|
|
2374
|
+
mem._setRaw("is_deleted", false);
|
|
2375
|
+
if (shouldPreserveEmbedding) {
|
|
2376
|
+
} else if (opts.embedding) {
|
|
2377
|
+
mem._setRaw("embedding", JSON.stringify(opts.embedding));
|
|
2378
|
+
if (opts.embeddingModel) {
|
|
2379
|
+
mem._setRaw("embedding_model", opts.embeddingModel);
|
|
2380
|
+
}
|
|
2381
|
+
} else {
|
|
2382
|
+
mem._setRaw("embedding", null);
|
|
2383
|
+
mem._setRaw("embedding_model", null);
|
|
2384
|
+
}
|
|
2385
|
+
});
|
|
2386
|
+
return existingMemory;
|
|
2387
|
+
}
|
|
2388
|
+
return await ctx.memoriesCollection.create((mem) => {
|
|
2389
|
+
mem._setRaw("type", opts.type);
|
|
2390
|
+
mem._setRaw("namespace", opts.namespace);
|
|
2391
|
+
mem._setRaw("key", opts.key);
|
|
2392
|
+
mem._setRaw("value", opts.value);
|
|
2393
|
+
mem._setRaw("raw_evidence", opts.rawEvidence);
|
|
2394
|
+
mem._setRaw("confidence", opts.confidence);
|
|
2395
|
+
mem._setRaw("pii", opts.pii);
|
|
2396
|
+
mem._setRaw("composite_key", compositeKey);
|
|
2397
|
+
mem._setRaw("unique_key", uniqueKey);
|
|
2398
|
+
mem._setRaw("is_deleted", false);
|
|
2399
|
+
if (opts.embedding) {
|
|
2400
|
+
mem._setRaw("embedding", JSON.stringify(opts.embedding));
|
|
2401
|
+
if (opts.embeddingModel) {
|
|
2402
|
+
mem._setRaw("embedding_model", opts.embeddingModel);
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
});
|
|
2406
|
+
});
|
|
2407
|
+
return memoryToStored(result);
|
|
2408
|
+
}
|
|
2409
|
+
async function saveMemoriesOp(ctx, memories) {
|
|
2410
|
+
const results = [];
|
|
2411
|
+
for (const memory of memories) {
|
|
2412
|
+
const saved = await saveMemoryOp(ctx, memory);
|
|
2413
|
+
results.push(saved);
|
|
2414
|
+
}
|
|
2415
|
+
return results;
|
|
2416
|
+
}
|
|
2417
|
+
async function updateMemoryOp(ctx, id, updates) {
|
|
2418
|
+
let memory;
|
|
2419
|
+
try {
|
|
2420
|
+
memory = await ctx.memoriesCollection.find(id);
|
|
2421
|
+
} catch {
|
|
2422
|
+
return { ok: false, reason: "not_found" };
|
|
2423
|
+
}
|
|
2424
|
+
if (memory.isDeleted) {
|
|
2425
|
+
return { ok: false, reason: "not_found" };
|
|
2426
|
+
}
|
|
2427
|
+
const newNamespace = updates.namespace ?? memory.namespace;
|
|
2428
|
+
const newKey = updates.key ?? memory.key;
|
|
2429
|
+
const newValue = updates.value ?? memory.value;
|
|
2430
|
+
const newCompositeKey = generateCompositeKey(newNamespace, newKey);
|
|
2431
|
+
const newUniqueKey = generateUniqueKey(newNamespace, newKey, newValue);
|
|
2432
|
+
if (newUniqueKey !== memory.uniqueKey) {
|
|
2433
|
+
const existing = await ctx.memoriesCollection.query(Q2.where("unique_key", newUniqueKey), Q2.where("is_deleted", false)).fetch();
|
|
2434
|
+
if (existing.length > 0) {
|
|
2435
|
+
return { ok: false, reason: "conflict", conflictingKey: newUniqueKey };
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
try {
|
|
2439
|
+
const updated = await ctx.database.write(async () => {
|
|
2440
|
+
await memory.update((mem) => {
|
|
2441
|
+
if (updates.type !== void 0) mem._setRaw("type", updates.type);
|
|
2442
|
+
if (updates.namespace !== void 0)
|
|
2443
|
+
mem._setRaw("namespace", updates.namespace);
|
|
2444
|
+
if (updates.key !== void 0) mem._setRaw("key", updates.key);
|
|
2445
|
+
if (updates.value !== void 0) mem._setRaw("value", updates.value);
|
|
2446
|
+
if (updates.rawEvidence !== void 0)
|
|
2447
|
+
mem._setRaw("raw_evidence", updates.rawEvidence);
|
|
2448
|
+
if (updates.confidence !== void 0)
|
|
2449
|
+
mem._setRaw("confidence", updates.confidence);
|
|
2450
|
+
if (updates.pii !== void 0) mem._setRaw("pii", updates.pii);
|
|
2451
|
+
if (updates.namespace !== void 0 || updates.key !== void 0 || updates.value !== void 0) {
|
|
2452
|
+
mem._setRaw("composite_key", newCompositeKey);
|
|
2453
|
+
mem._setRaw("unique_key", newUniqueKey);
|
|
2454
|
+
}
|
|
2455
|
+
if (updates.embedding !== void 0) {
|
|
2456
|
+
mem._setRaw(
|
|
2457
|
+
"embedding",
|
|
2458
|
+
updates.embedding ? JSON.stringify(updates.embedding) : null
|
|
2459
|
+
);
|
|
2460
|
+
}
|
|
2461
|
+
if (updates.embeddingModel !== void 0) {
|
|
2462
|
+
mem._setRaw("embedding_model", updates.embeddingModel || null);
|
|
2463
|
+
}
|
|
2464
|
+
});
|
|
2465
|
+
return memory;
|
|
2466
|
+
});
|
|
2467
|
+
return { ok: true, memory: memoryToStored(updated) };
|
|
2468
|
+
} catch (err) {
|
|
2469
|
+
return {
|
|
2470
|
+
ok: false,
|
|
2471
|
+
reason: "error",
|
|
2472
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
2473
|
+
};
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
async function deleteMemoryByIdOp(ctx, id) {
|
|
2477
|
+
try {
|
|
2478
|
+
const memory = await ctx.memoriesCollection.find(id);
|
|
2479
|
+
await ctx.database.write(async () => {
|
|
2480
|
+
await memory.update((mem) => {
|
|
2481
|
+
mem._setRaw("is_deleted", true);
|
|
2482
|
+
});
|
|
2483
|
+
});
|
|
2484
|
+
} catch {
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
async function deleteMemoryOp(ctx, namespace, key, value) {
|
|
2488
|
+
const uniqueKey = generateUniqueKey(namespace, key, value);
|
|
2489
|
+
const results = await ctx.memoriesCollection.query(Q2.where("unique_key", uniqueKey)).fetch();
|
|
2490
|
+
if (results.length > 0) {
|
|
2491
|
+
await ctx.database.write(async () => {
|
|
2492
|
+
await results[0].update((mem) => {
|
|
2493
|
+
mem._setRaw("is_deleted", true);
|
|
2494
|
+
});
|
|
2495
|
+
});
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
async function deleteMemoriesByKeyOp(ctx, namespace, key) {
|
|
2499
|
+
const compositeKey = generateCompositeKey(namespace, key);
|
|
2500
|
+
const results = await ctx.memoriesCollection.query(Q2.where("composite_key", compositeKey), Q2.where("is_deleted", false)).fetch();
|
|
2501
|
+
await ctx.database.write(async () => {
|
|
2502
|
+
for (const memory of results) {
|
|
2503
|
+
await memory.update((mem) => {
|
|
2504
|
+
mem._setRaw("is_deleted", true);
|
|
2505
|
+
});
|
|
2506
|
+
}
|
|
2507
|
+
});
|
|
2508
|
+
}
|
|
2509
|
+
async function clearAllMemoriesOp(ctx) {
|
|
2510
|
+
const results = await ctx.memoriesCollection.query(Q2.where("is_deleted", false)).fetch();
|
|
2511
|
+
await ctx.database.write(async () => {
|
|
2512
|
+
for (const memory of results) {
|
|
2513
|
+
await memory.update((mem) => {
|
|
2514
|
+
mem._setRaw("is_deleted", true);
|
|
2515
|
+
});
|
|
2516
|
+
}
|
|
2517
|
+
});
|
|
2518
|
+
}
|
|
2519
|
+
async function searchSimilarMemoriesOp(ctx, queryEmbedding, limit = 10, minSimilarity = 0.6) {
|
|
2520
|
+
const allMemories = await ctx.memoriesCollection.query(Q2.where("is_deleted", false)).fetch();
|
|
2521
|
+
const memoriesWithEmbeddings = allMemories.filter(
|
|
2522
|
+
(m) => m.embedding && m.embedding.length > 0
|
|
2523
|
+
);
|
|
2524
|
+
if (memoriesWithEmbeddings.length === 0) {
|
|
2525
|
+
return [];
|
|
2526
|
+
}
|
|
2527
|
+
const results = memoriesWithEmbeddings.map((memory) => {
|
|
2528
|
+
const similarity = cosineSimilarity(queryEmbedding, memory.embedding);
|
|
2529
|
+
return {
|
|
2530
|
+
...memoryToStored(memory),
|
|
2531
|
+
similarity
|
|
2532
|
+
};
|
|
2533
|
+
}).filter((result) => result.similarity >= minSimilarity).sort((a, b) => b.similarity - a.similarity).slice(0, limit);
|
|
2534
|
+
return results;
|
|
2535
|
+
}
|
|
2536
|
+
async function updateMemoryEmbeddingOp(ctx, id, embedding, embeddingModel) {
|
|
2537
|
+
try {
|
|
2538
|
+
const memory = await ctx.memoriesCollection.find(id);
|
|
2539
|
+
await ctx.database.write(async () => {
|
|
2540
|
+
await memory.update((mem) => {
|
|
2541
|
+
mem._setRaw("embedding", JSON.stringify(embedding));
|
|
2542
|
+
mem._setRaw("embedding_model", embeddingModel);
|
|
2543
|
+
});
|
|
2544
|
+
});
|
|
2545
|
+
} catch {
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
// src/lib/memory/service.ts
|
|
2550
|
+
var FACT_EXTRACTION_PROMPT = `You are a memory extraction system. Extract durable user memories from chat messages.
|
|
2551
|
+
|
|
2552
|
+
CRITICAL: You MUST respond with ONLY valid JSON. No explanations, no markdown, no code blocks, just pure JSON.
|
|
2553
|
+
|
|
2554
|
+
Only store clear, factual statements that might be relevant for future context or reference. Extract facts that will be useful in future conversations, such as identity, stable preferences, ongoing projects, skills, locations, favorites, and constraints.
|
|
2555
|
+
|
|
2556
|
+
Do not extract sensitive attributes, temporary things, or single-use instructions.
|
|
2557
|
+
|
|
2558
|
+
You must also extract stable personal preferences, including food likes/dislikes, hobbies, favorite items, favorite genres, or other enduring tastes.
|
|
2559
|
+
|
|
2560
|
+
If there are no memories to extract, return: {"items": []}
|
|
2561
|
+
|
|
2562
|
+
Response format (JSON only, no other text):
|
|
2563
|
+
|
|
2564
|
+
{
|
|
2565
|
+
"items": [
|
|
2566
|
+
{
|
|
2567
|
+
"type": "identity",
|
|
2568
|
+
"namespace": "identity",
|
|
2569
|
+
"key": "name",
|
|
2570
|
+
"value": "Charlie",
|
|
2571
|
+
"rawEvidence": "I'm Charlie",
|
|
2572
|
+
"confidence": 0.98,
|
|
2573
|
+
"pii": true
|
|
2574
|
+
},
|
|
2575
|
+
{
|
|
2576
|
+
"type": "identity",
|
|
2577
|
+
"namespace": "work",
|
|
2578
|
+
"key": "company",
|
|
2579
|
+
"value": "ZetaChain",
|
|
2580
|
+
"rawEvidence": "called ZetaChain",
|
|
2581
|
+
"confidence": 0.99,
|
|
2582
|
+
"pii": false
|
|
2583
|
+
},
|
|
2584
|
+
{
|
|
2585
|
+
"type": "identity",
|
|
2586
|
+
"namespace": "location",
|
|
2587
|
+
"key": "city",
|
|
2588
|
+
"value": "San Francisco",
|
|
2589
|
+
"rawEvidence": "I live in San Francisco",
|
|
2590
|
+
"confidence": 0.99,
|
|
2591
|
+
"pii": false
|
|
2592
|
+
},
|
|
2593
|
+
{
|
|
2594
|
+
"type": "preference",
|
|
2595
|
+
"namespace": "location",
|
|
2596
|
+
"key": "country",
|
|
2597
|
+
"value": "Japan",
|
|
2598
|
+
"rawEvidence": "I like to travel to the Japan",
|
|
2599
|
+
"confidence": 0.94,
|
|
2600
|
+
"pii": false
|
|
2601
|
+
},
|
|
2602
|
+
{
|
|
2603
|
+
"type": "preference",
|
|
2604
|
+
"namespace": "answer_style",
|
|
2605
|
+
"key": "verbosity",
|
|
2606
|
+
"value": "concise_direct",
|
|
2607
|
+
"rawEvidence": "I prefer concise, direct answers",
|
|
2608
|
+
"confidence": 0.96,
|
|
2609
|
+
"pii": false
|
|
2610
|
+
},
|
|
2611
|
+
{
|
|
2612
|
+
"type": "identity",
|
|
2613
|
+
"namespace": "timezone",
|
|
2614
|
+
"key": "tz",
|
|
2615
|
+
"value": "America/Los_Angeles",
|
|
2616
|
+
"rawEvidence": "I'm in PST",
|
|
2617
|
+
"confidence": 0.9,
|
|
2618
|
+
"pii": false
|
|
2619
|
+
},
|
|
2620
|
+
{
|
|
2621
|
+
"type": "preference",
|
|
2622
|
+
"namespace": "food",
|
|
2623
|
+
"key": "likes_ice_cream",
|
|
2624
|
+
"value": "ice cream",
|
|
2625
|
+
"rawEvidence": "I like ice cream",
|
|
2626
|
+
"confidence": 0.95,
|
|
2627
|
+
"pii": false
|
|
2628
|
+
}
|
|
2629
|
+
]
|
|
2630
|
+
}`;
|
|
2631
|
+
var preprocessMemories = (items, minConfidence = 0.6) => {
|
|
2632
|
+
if (!items || !Array.isArray(items)) {
|
|
2633
|
+
return [];
|
|
2634
|
+
}
|
|
2635
|
+
const validItems = items.filter((item) => {
|
|
2636
|
+
if (item.namespace == null || item.key == null || item.value == null) {
|
|
2637
|
+
console.warn(
|
|
2638
|
+
"Dropping memory item with null/undefined namespace, key, or value:",
|
|
2639
|
+
item
|
|
2640
|
+
);
|
|
2641
|
+
return false;
|
|
2642
|
+
}
|
|
2643
|
+
const namespace = String(item.namespace).trim();
|
|
2644
|
+
const key = String(item.key).trim();
|
|
2645
|
+
const value = String(item.value).trim();
|
|
2646
|
+
if (namespace === "" || key === "" || value === "") {
|
|
2647
|
+
console.warn(
|
|
2648
|
+
"Dropping memory item with empty namespace, key, or value after trimming:",
|
|
2649
|
+
item
|
|
2650
|
+
);
|
|
2651
|
+
return false;
|
|
2652
|
+
}
|
|
2653
|
+
if (typeof item.confidence !== "number" || item.confidence < minConfidence) {
|
|
2654
|
+
console.warn(
|
|
2655
|
+
`Dropping memory item with confidence ${item.confidence} below threshold ${minConfidence}:`,
|
|
2656
|
+
item
|
|
2657
|
+
);
|
|
2658
|
+
return false;
|
|
2659
|
+
}
|
|
2660
|
+
return true;
|
|
2661
|
+
});
|
|
2662
|
+
const deduplicatedMap = /* @__PURE__ */ new Map();
|
|
2663
|
+
for (const item of validItems) {
|
|
2664
|
+
const uniqueKey = `${item.namespace}:${item.key}:${item.value}`;
|
|
2665
|
+
const existing = deduplicatedMap.get(uniqueKey);
|
|
2666
|
+
if (!existing || item.confidence > existing.confidence) {
|
|
2667
|
+
deduplicatedMap.set(uniqueKey, item);
|
|
2668
|
+
} else {
|
|
2669
|
+
console.debug(
|
|
2670
|
+
`Deduplicating memory item: keeping entry with higher confidence (${existing.confidence} > ${item.confidence})`,
|
|
2671
|
+
{ namespace: item.namespace, key: item.key, value: item.value }
|
|
2672
|
+
);
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
return Array.from(deduplicatedMap.values());
|
|
2676
|
+
};
|
|
2677
|
+
|
|
2678
|
+
// src/lib/memory/constants.ts
|
|
2679
|
+
var DEFAULT_API_EMBEDDING_MODEL = "openai/text-embedding-3-small";
|
|
2680
|
+
var DEFAULT_COMPLETION_MODEL = "openai/gpt-4o";
|
|
2681
|
+
|
|
2682
|
+
// src/expo/useMemoryStorage.ts
|
|
2683
|
+
import { postApiV1Embeddings } from "@reverbia/sdk";
|
|
2684
|
+
async function generateEmbeddingForTextApi(text4, options) {
|
|
2685
|
+
const token = options.getToken ? await options.getToken() : null;
|
|
2686
|
+
if (!token) {
|
|
2687
|
+
throw new Error("No auth token available for embedding generation");
|
|
2688
|
+
}
|
|
2689
|
+
const response = await postApiV1Embeddings({
|
|
2690
|
+
baseUrl: options.baseUrl,
|
|
2691
|
+
body: {
|
|
2692
|
+
input: text4,
|
|
2693
|
+
model: options.model
|
|
2694
|
+
},
|
|
2695
|
+
headers: {
|
|
2696
|
+
Authorization: `Bearer ${token}`
|
|
2697
|
+
}
|
|
2698
|
+
});
|
|
2699
|
+
if (!response.data || typeof response.data === "string") {
|
|
2700
|
+
throw new Error("Failed to generate embedding");
|
|
2701
|
+
}
|
|
2702
|
+
const embedding = response.data.data?.[0]?.embedding;
|
|
2703
|
+
if (!embedding) {
|
|
2704
|
+
throw new Error("No embedding in response");
|
|
2705
|
+
}
|
|
2706
|
+
return embedding;
|
|
2707
|
+
}
|
|
2708
|
+
async function generateEmbeddingForMemoryApi(memory, options) {
|
|
2709
|
+
const text4 = `${memory.type}: ${memory.namespace}/${memory.key} = ${memory.value}. Evidence: ${memory.rawEvidence}`;
|
|
2710
|
+
return generateEmbeddingForTextApi(text4, options);
|
|
2711
|
+
}
|
|
2712
|
+
function useMemoryStorage(options) {
|
|
2713
|
+
const {
|
|
2714
|
+
database,
|
|
2715
|
+
completionsModel = DEFAULT_COMPLETION_MODEL,
|
|
2716
|
+
embeddingModel: userEmbeddingModel,
|
|
2717
|
+
generateEmbeddings = true,
|
|
2718
|
+
onFactsExtracted,
|
|
2719
|
+
getToken,
|
|
2720
|
+
baseUrl = BASE_URL
|
|
2721
|
+
} = options;
|
|
2722
|
+
const embeddingModel = userEmbeddingModel === void 0 ? DEFAULT_API_EMBEDDING_MODEL : userEmbeddingModel;
|
|
2723
|
+
const [memories, setMemories] = useState5([]);
|
|
2724
|
+
const extractionInProgressRef = useRef4(false);
|
|
2725
|
+
const memoriesCollection = useMemo2(
|
|
2726
|
+
() => database.get("memories"),
|
|
2727
|
+
[database]
|
|
2728
|
+
);
|
|
2729
|
+
const storageCtx = useMemo2(
|
|
2730
|
+
() => ({
|
|
2731
|
+
database,
|
|
2732
|
+
memoriesCollection
|
|
2733
|
+
}),
|
|
2734
|
+
[database, memoriesCollection]
|
|
2735
|
+
);
|
|
2736
|
+
const embeddingOptions = useMemo2(
|
|
2737
|
+
() => ({
|
|
2738
|
+
model: embeddingModel ?? DEFAULT_API_EMBEDDING_MODEL,
|
|
2739
|
+
getToken: getToken || void 0,
|
|
2740
|
+
baseUrl
|
|
2741
|
+
}),
|
|
2742
|
+
[embeddingModel, getToken, baseUrl]
|
|
2743
|
+
);
|
|
2744
|
+
const refreshMemories = useCallback5(async () => {
|
|
2745
|
+
const storedMemories = await getAllMemoriesOp(storageCtx);
|
|
2746
|
+
setMemories(storedMemories);
|
|
2747
|
+
}, [storageCtx]);
|
|
2748
|
+
const extractMemoriesFromMessage = useCallback5(
|
|
2749
|
+
async (opts) => {
|
|
2750
|
+
const { messages, model } = opts;
|
|
2751
|
+
if (!getToken || extractionInProgressRef.current) {
|
|
2752
|
+
return null;
|
|
2753
|
+
}
|
|
2754
|
+
extractionInProgressRef.current = true;
|
|
2755
|
+
try {
|
|
2756
|
+
const token = await getToken();
|
|
2757
|
+
if (!token) {
|
|
2758
|
+
console.error("No access token available for memory extraction");
|
|
2759
|
+
return null;
|
|
2760
|
+
}
|
|
2761
|
+
const completion = await postApiV1ChatCompletions({
|
|
2762
|
+
baseUrl,
|
|
2763
|
+
body: {
|
|
2764
|
+
messages: [
|
|
2765
|
+
{
|
|
2766
|
+
role: "system",
|
|
2767
|
+
content: [{ type: "text", text: FACT_EXTRACTION_PROMPT }]
|
|
2768
|
+
},
|
|
2769
|
+
...messages.map((m) => ({
|
|
2770
|
+
role: m.role,
|
|
2771
|
+
content: [{ type: "text", text: m.content }]
|
|
2772
|
+
}))
|
|
2773
|
+
],
|
|
2774
|
+
model: model || completionsModel
|
|
2775
|
+
},
|
|
2776
|
+
headers: {
|
|
2777
|
+
Authorization: `Bearer ${token}`
|
|
2778
|
+
}
|
|
2779
|
+
});
|
|
2780
|
+
if (!completion.data) {
|
|
2781
|
+
console.error(
|
|
2782
|
+
"Memory extraction failed:",
|
|
2783
|
+
completion.error?.error ?? "API did not return a response"
|
|
2784
|
+
);
|
|
2785
|
+
return null;
|
|
2786
|
+
}
|
|
2787
|
+
if (typeof completion.data === "string") {
|
|
2788
|
+
console.error(
|
|
2789
|
+
"Memory extraction failed: API returned a string response"
|
|
2790
|
+
);
|
|
2791
|
+
return null;
|
|
2792
|
+
}
|
|
2793
|
+
const messageContent = completion.data.choices?.[0]?.message?.content;
|
|
2794
|
+
let content = "";
|
|
2795
|
+
if (Array.isArray(messageContent)) {
|
|
2796
|
+
content = messageContent.map((p) => p.text || "").join("").trim();
|
|
2797
|
+
} else if (typeof messageContent === "string") {
|
|
2798
|
+
content = messageContent.trim();
|
|
2799
|
+
}
|
|
2800
|
+
if (!content) {
|
|
2801
|
+
console.error("No content in memory extraction response");
|
|
2802
|
+
return null;
|
|
2803
|
+
}
|
|
2804
|
+
let jsonContent = content;
|
|
2805
|
+
jsonContent = jsonContent.replace(/^data:\s*/gm, "").trim();
|
|
2806
|
+
if (jsonContent.startsWith("{")) {
|
|
2807
|
+
let braceCount = 0;
|
|
2808
|
+
let jsonStart = -1;
|
|
2809
|
+
let jsonEnd = -1;
|
|
2810
|
+
for (let i = 0; i < jsonContent.length; i++) {
|
|
2811
|
+
if (jsonContent[i] === "{") {
|
|
2812
|
+
if (jsonStart === -1) jsonStart = i;
|
|
2813
|
+
braceCount++;
|
|
2814
|
+
} else if (jsonContent[i] === "}") {
|
|
2815
|
+
braceCount--;
|
|
2816
|
+
if (braceCount === 0 && jsonStart !== -1) {
|
|
2817
|
+
jsonEnd = i + 1;
|
|
2818
|
+
break;
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
if (jsonStart !== -1 && jsonEnd !== -1) {
|
|
2823
|
+
jsonContent = jsonContent.substring(jsonStart, jsonEnd);
|
|
2824
|
+
}
|
|
2825
|
+
} else {
|
|
2826
|
+
const jsonMatch = jsonContent.match(
|
|
2827
|
+
/```(?:json)?\s*(\{[\s\S]*?\})\s*```/
|
|
2828
|
+
);
|
|
2829
|
+
if (jsonMatch && jsonMatch[1]) {
|
|
2830
|
+
jsonContent = jsonMatch[1].trim();
|
|
2831
|
+
} else {
|
|
2832
|
+
const jsonObjectMatch = jsonContent.match(/\{[\s\S]*\}/);
|
|
2833
|
+
if (jsonObjectMatch && jsonObjectMatch[0]) {
|
|
2834
|
+
jsonContent = jsonObjectMatch[0];
|
|
2835
|
+
} else {
|
|
2836
|
+
console.warn("Memory extraction returned non-JSON response.");
|
|
2837
|
+
return { items: [] };
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
const trimmedJson = jsonContent.trim();
|
|
2842
|
+
if (!trimmedJson.startsWith("{") || !trimmedJson.includes("items")) {
|
|
2843
|
+
console.warn("Memory extraction response doesn't appear to be valid JSON.");
|
|
2844
|
+
return { items: [] };
|
|
2845
|
+
}
|
|
2846
|
+
let result;
|
|
2847
|
+
try {
|
|
2848
|
+
result = JSON.parse(jsonContent);
|
|
2849
|
+
if (!result || typeof result !== "object") {
|
|
2850
|
+
throw new Error("Invalid JSON structure: not an object");
|
|
2851
|
+
}
|
|
2852
|
+
if (!Array.isArray(result.items)) {
|
|
2853
|
+
console.warn("Memory extraction result missing 'items' array.");
|
|
2854
|
+
return { items: [] };
|
|
2855
|
+
}
|
|
2856
|
+
} catch (parseError) {
|
|
2857
|
+
console.error(
|
|
2858
|
+
"Failed to parse memory extraction JSON:",
|
|
2859
|
+
parseError instanceof Error ? parseError.message : "Unknown error"
|
|
2860
|
+
);
|
|
2861
|
+
return { items: [] };
|
|
2862
|
+
}
|
|
2863
|
+
if (result.items && Array.isArray(result.items)) {
|
|
2864
|
+
result.items = preprocessMemories(result.items);
|
|
2865
|
+
}
|
|
2866
|
+
if (result.items && result.items.length > 0) {
|
|
2867
|
+
try {
|
|
2868
|
+
const createOptions = result.items.map(
|
|
2869
|
+
(item) => ({
|
|
2870
|
+
type: item.type,
|
|
2871
|
+
namespace: item.namespace,
|
|
2872
|
+
key: item.key,
|
|
2873
|
+
value: item.value,
|
|
2874
|
+
rawEvidence: item.rawEvidence,
|
|
2875
|
+
confidence: item.confidence,
|
|
2876
|
+
pii: item.pii
|
|
2877
|
+
})
|
|
2878
|
+
);
|
|
2879
|
+
const savedMemories = await saveMemoriesOp(storageCtx, createOptions);
|
|
2880
|
+
console.log(`Saved ${savedMemories.length} memories to WatermelonDB`);
|
|
2881
|
+
if (generateEmbeddings && embeddingModel) {
|
|
2882
|
+
try {
|
|
2883
|
+
for (const saved of savedMemories) {
|
|
2884
|
+
const memoryItem = {
|
|
2885
|
+
type: saved.type,
|
|
2886
|
+
namespace: saved.namespace,
|
|
2887
|
+
key: saved.key,
|
|
2888
|
+
value: saved.value,
|
|
2889
|
+
rawEvidence: saved.rawEvidence,
|
|
2890
|
+
confidence: saved.confidence,
|
|
2891
|
+
pii: saved.pii
|
|
2892
|
+
};
|
|
2893
|
+
const embedding = await generateEmbeddingForMemoryApi(
|
|
2894
|
+
memoryItem,
|
|
2895
|
+
embeddingOptions
|
|
2896
|
+
);
|
|
2897
|
+
await updateMemoryEmbeddingOp(
|
|
2898
|
+
storageCtx,
|
|
2899
|
+
saved.uniqueId,
|
|
2900
|
+
embedding,
|
|
2901
|
+
embeddingOptions.model
|
|
2902
|
+
);
|
|
2903
|
+
}
|
|
2904
|
+
console.log(`Generated embeddings for ${savedMemories.length} memories`);
|
|
2905
|
+
} catch (error) {
|
|
2906
|
+
console.error("Failed to generate embeddings:", error);
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
await refreshMemories();
|
|
2910
|
+
} catch (error) {
|
|
2911
|
+
console.error("Failed to save memories to WatermelonDB:", error);
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
if (onFactsExtracted) {
|
|
2915
|
+
onFactsExtracted(result);
|
|
2916
|
+
}
|
|
2917
|
+
return result;
|
|
2918
|
+
} catch (error) {
|
|
2919
|
+
console.error("Failed to extract facts:", error);
|
|
2920
|
+
return null;
|
|
2921
|
+
} finally {
|
|
2922
|
+
extractionInProgressRef.current = false;
|
|
2923
|
+
}
|
|
2924
|
+
},
|
|
2925
|
+
[
|
|
2926
|
+
completionsModel,
|
|
2927
|
+
embeddingModel,
|
|
2928
|
+
embeddingOptions,
|
|
2929
|
+
generateEmbeddings,
|
|
2930
|
+
getToken,
|
|
2931
|
+
onFactsExtracted,
|
|
2932
|
+
baseUrl,
|
|
2933
|
+
storageCtx,
|
|
2934
|
+
refreshMemories
|
|
2935
|
+
]
|
|
2936
|
+
);
|
|
2937
|
+
const searchMemories = useCallback5(
|
|
2938
|
+
async (query, limit = 10, minSimilarity = 0.6) => {
|
|
2939
|
+
if (!embeddingModel) {
|
|
2940
|
+
console.warn("Cannot search memories: embeddingModel not provided");
|
|
2941
|
+
return [];
|
|
2942
|
+
}
|
|
2943
|
+
try {
|
|
2944
|
+
const queryEmbedding = await generateEmbeddingForTextApi(
|
|
2945
|
+
query,
|
|
2946
|
+
embeddingOptions
|
|
2947
|
+
);
|
|
2948
|
+
const results = await searchSimilarMemoriesOp(
|
|
2949
|
+
storageCtx,
|
|
2950
|
+
queryEmbedding,
|
|
2951
|
+
limit,
|
|
2952
|
+
minSimilarity
|
|
2953
|
+
);
|
|
2954
|
+
if (results.length === 0) {
|
|
2955
|
+
console.warn(
|
|
2956
|
+
`[Memory Search] No memories found above similarity threshold ${minSimilarity}.`
|
|
2957
|
+
);
|
|
2958
|
+
} else {
|
|
2959
|
+
console.log(
|
|
2960
|
+
`[Memory Search] Found ${results.length} memories. Similarity scores: ${results.map((r) => r.similarity.toFixed(3)).join(", ")}`
|
|
2961
|
+
);
|
|
2962
|
+
}
|
|
2963
|
+
return results;
|
|
2964
|
+
} catch {
|
|
2965
|
+
return [];
|
|
2966
|
+
}
|
|
2967
|
+
},
|
|
2968
|
+
[embeddingModel, embeddingOptions, storageCtx]
|
|
2969
|
+
);
|
|
2970
|
+
const fetchAllMemories = useCallback5(async () => {
|
|
2971
|
+
try {
|
|
2972
|
+
return await getAllMemoriesOp(storageCtx);
|
|
2973
|
+
} catch (error) {
|
|
2974
|
+
throw new Error(
|
|
2975
|
+
"Failed to fetch all memories: " + (error instanceof Error ? error.message : String(error))
|
|
2976
|
+
);
|
|
2977
|
+
}
|
|
2978
|
+
}, [storageCtx]);
|
|
2979
|
+
const fetchMemoriesByNamespace = useCallback5(
|
|
2980
|
+
async (namespace) => {
|
|
2981
|
+
if (!namespace) {
|
|
2982
|
+
throw new Error("Missing required field: namespace");
|
|
2983
|
+
}
|
|
2984
|
+
try {
|
|
2985
|
+
return await getMemoriesByNamespaceOp(storageCtx, namespace);
|
|
2986
|
+
} catch (error) {
|
|
2987
|
+
throw new Error(
|
|
2988
|
+
`Failed to fetch memories for namespace "${namespace}": ` + (error instanceof Error ? error.message : String(error))
|
|
2989
|
+
);
|
|
2990
|
+
}
|
|
2991
|
+
},
|
|
2992
|
+
[storageCtx]
|
|
2993
|
+
);
|
|
2994
|
+
const fetchMemoriesByKey = useCallback5(
|
|
2995
|
+
async (namespace, key) => {
|
|
2996
|
+
if (!namespace || !key) {
|
|
2997
|
+
throw new Error("Missing required fields: namespace, key");
|
|
2998
|
+
}
|
|
2999
|
+
try {
|
|
3000
|
+
return await getMemoriesByKeyOp(storageCtx, namespace, key);
|
|
3001
|
+
} catch (error) {
|
|
3002
|
+
throw new Error(
|
|
3003
|
+
`Failed to fetch memories for "${namespace}:${key}": ` + (error instanceof Error ? error.message : String(error))
|
|
3004
|
+
);
|
|
3005
|
+
}
|
|
3006
|
+
},
|
|
3007
|
+
[storageCtx]
|
|
3008
|
+
);
|
|
3009
|
+
const getMemoryById = useCallback5(
|
|
3010
|
+
async (id) => {
|
|
3011
|
+
try {
|
|
3012
|
+
return await getMemoryByIdOp(storageCtx, id);
|
|
3013
|
+
} catch (error) {
|
|
3014
|
+
throw new Error(
|
|
3015
|
+
`Failed to get memory ${id}: ` + (error instanceof Error ? error.message : String(error))
|
|
3016
|
+
);
|
|
3017
|
+
}
|
|
3018
|
+
},
|
|
3019
|
+
[storageCtx]
|
|
3020
|
+
);
|
|
3021
|
+
const saveMemory = useCallback5(
|
|
3022
|
+
async (memory) => {
|
|
3023
|
+
try {
|
|
3024
|
+
const saved = await saveMemoryOp(storageCtx, memory);
|
|
3025
|
+
if (generateEmbeddings && embeddingModel && !memory.embedding) {
|
|
3026
|
+
try {
|
|
3027
|
+
const memoryItem = {
|
|
3028
|
+
type: memory.type,
|
|
3029
|
+
namespace: memory.namespace,
|
|
3030
|
+
key: memory.key,
|
|
3031
|
+
value: memory.value,
|
|
3032
|
+
rawEvidence: memory.rawEvidence,
|
|
3033
|
+
confidence: memory.confidence,
|
|
3034
|
+
pii: memory.pii
|
|
3035
|
+
};
|
|
3036
|
+
const embedding = await generateEmbeddingForMemoryApi(
|
|
3037
|
+
memoryItem,
|
|
3038
|
+
embeddingOptions
|
|
3039
|
+
);
|
|
3040
|
+
await updateMemoryEmbeddingOp(
|
|
3041
|
+
storageCtx,
|
|
3042
|
+
saved.uniqueId,
|
|
3043
|
+
embedding,
|
|
3044
|
+
embeddingOptions.model
|
|
3045
|
+
);
|
|
3046
|
+
} catch (error) {
|
|
3047
|
+
console.error("Failed to generate embedding:", error);
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
setMemories((prev) => {
|
|
3051
|
+
const existing = prev.find((m) => m.uniqueId === saved.uniqueId);
|
|
3052
|
+
if (existing) {
|
|
3053
|
+
return prev.map((m) => m.uniqueId === saved.uniqueId ? saved : m);
|
|
3054
|
+
}
|
|
3055
|
+
return [saved, ...prev];
|
|
3056
|
+
});
|
|
3057
|
+
return saved;
|
|
3058
|
+
} catch (error) {
|
|
3059
|
+
throw new Error(
|
|
3060
|
+
"Failed to save memory: " + (error instanceof Error ? error.message : String(error))
|
|
3061
|
+
);
|
|
3062
|
+
}
|
|
3063
|
+
},
|
|
3064
|
+
[storageCtx, generateEmbeddings, embeddingModel, embeddingOptions]
|
|
3065
|
+
);
|
|
3066
|
+
const saveMemories = useCallback5(
|
|
3067
|
+
async (memoriesToSave) => {
|
|
3068
|
+
try {
|
|
3069
|
+
const saved = await saveMemoriesOp(storageCtx, memoriesToSave);
|
|
3070
|
+
if (generateEmbeddings && embeddingModel) {
|
|
3071
|
+
for (let i = 0; i < saved.length; i++) {
|
|
3072
|
+
const memory = memoriesToSave[i];
|
|
3073
|
+
if (!memory.embedding) {
|
|
3074
|
+
try {
|
|
3075
|
+
const memoryItem = {
|
|
3076
|
+
type: memory.type,
|
|
3077
|
+
namespace: memory.namespace,
|
|
3078
|
+
key: memory.key,
|
|
3079
|
+
value: memory.value,
|
|
3080
|
+
rawEvidence: memory.rawEvidence,
|
|
3081
|
+
confidence: memory.confidence,
|
|
3082
|
+
pii: memory.pii
|
|
3083
|
+
};
|
|
3084
|
+
const embedding = await generateEmbeddingForMemoryApi(
|
|
3085
|
+
memoryItem,
|
|
3086
|
+
embeddingOptions
|
|
3087
|
+
);
|
|
3088
|
+
await updateMemoryEmbeddingOp(
|
|
3089
|
+
storageCtx,
|
|
3090
|
+
saved[i].uniqueId,
|
|
3091
|
+
embedding,
|
|
3092
|
+
embeddingOptions.model
|
|
3093
|
+
);
|
|
3094
|
+
} catch (error) {
|
|
3095
|
+
console.error("Failed to generate embedding:", error);
|
|
3096
|
+
}
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
await refreshMemories();
|
|
3101
|
+
return saved;
|
|
3102
|
+
} catch (error) {
|
|
3103
|
+
throw new Error(
|
|
3104
|
+
"Failed to save memories: " + (error instanceof Error ? error.message : String(error))
|
|
3105
|
+
);
|
|
3106
|
+
}
|
|
3107
|
+
},
|
|
3108
|
+
[storageCtx, generateEmbeddings, embeddingModel, embeddingOptions, refreshMemories]
|
|
3109
|
+
);
|
|
3110
|
+
const updateMemory = useCallback5(
|
|
3111
|
+
async (id, updates) => {
|
|
3112
|
+
const result = await updateMemoryOp(storageCtx, id, updates);
|
|
3113
|
+
if (!result.ok) {
|
|
3114
|
+
if (result.reason === "not_found") {
|
|
3115
|
+
return null;
|
|
3116
|
+
}
|
|
3117
|
+
if (result.reason === "conflict") {
|
|
3118
|
+
throw new Error(
|
|
3119
|
+
`Cannot update memory: a memory with key "${result.conflictingKey}" already exists`
|
|
3120
|
+
);
|
|
3121
|
+
}
|
|
3122
|
+
throw new Error(
|
|
3123
|
+
`Failed to update memory ${id}: ${result.error.message}`
|
|
3124
|
+
);
|
|
3125
|
+
}
|
|
3126
|
+
const updated = result.memory;
|
|
3127
|
+
const contentChanged = updates.value !== void 0 || updates.rawEvidence !== void 0 || updates.type !== void 0 || updates.namespace !== void 0 || updates.key !== void 0;
|
|
3128
|
+
if (contentChanged && generateEmbeddings && embeddingModel && !updates.embedding) {
|
|
3129
|
+
try {
|
|
3130
|
+
const memoryItem = {
|
|
3131
|
+
type: updated.type,
|
|
3132
|
+
namespace: updated.namespace,
|
|
3133
|
+
key: updated.key,
|
|
3134
|
+
value: updated.value,
|
|
3135
|
+
rawEvidence: updated.rawEvidence,
|
|
3136
|
+
confidence: updated.confidence,
|
|
3137
|
+
pii: updated.pii
|
|
3138
|
+
};
|
|
3139
|
+
const embedding = await generateEmbeddingForMemoryApi(
|
|
3140
|
+
memoryItem,
|
|
3141
|
+
embeddingOptions
|
|
3142
|
+
);
|
|
3143
|
+
await updateMemoryEmbeddingOp(storageCtx, id, embedding, embeddingOptions.model);
|
|
3144
|
+
} catch (error) {
|
|
3145
|
+
console.error("Failed to regenerate embedding:", error);
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
3148
|
+
setMemories((prev) => prev.map((m) => m.uniqueId === id ? updated : m));
|
|
3149
|
+
return updated;
|
|
3150
|
+
},
|
|
3151
|
+
[storageCtx, generateEmbeddings, embeddingModel, embeddingOptions]
|
|
3152
|
+
);
|
|
3153
|
+
const removeMemory = useCallback5(
|
|
3154
|
+
async (namespace, key, value) => {
|
|
3155
|
+
if (!namespace || !key || !value) {
|
|
3156
|
+
throw new Error("Missing required fields: namespace, key, value");
|
|
3157
|
+
}
|
|
3158
|
+
try {
|
|
3159
|
+
await deleteMemoryOp(storageCtx, namespace, key, value);
|
|
3160
|
+
setMemories(
|
|
3161
|
+
(prev) => prev.filter(
|
|
3162
|
+
(m) => !(m.namespace === namespace && m.key === key && m.value === value)
|
|
3163
|
+
)
|
|
3164
|
+
);
|
|
3165
|
+
} catch (error) {
|
|
3166
|
+
throw new Error(
|
|
3167
|
+
`Failed to delete memory "${namespace}:${key}:${value}": ` + (error instanceof Error ? error.message : String(error))
|
|
3168
|
+
);
|
|
3169
|
+
}
|
|
3170
|
+
},
|
|
3171
|
+
[storageCtx]
|
|
3172
|
+
);
|
|
3173
|
+
const removeMemoryById = useCallback5(
|
|
3174
|
+
async (id) => {
|
|
3175
|
+
try {
|
|
3176
|
+
await deleteMemoryByIdOp(storageCtx, id);
|
|
3177
|
+
setMemories((prev) => prev.filter((m) => m.uniqueId !== id));
|
|
3178
|
+
} catch (error) {
|
|
3179
|
+
throw new Error(
|
|
3180
|
+
`Failed to delete memory with id ${id}: ` + (error instanceof Error ? error.message : String(error))
|
|
3181
|
+
);
|
|
3182
|
+
}
|
|
3183
|
+
},
|
|
3184
|
+
[storageCtx]
|
|
3185
|
+
);
|
|
3186
|
+
const removeMemories = useCallback5(
|
|
3187
|
+
async (namespace, key) => {
|
|
3188
|
+
if (!namespace || !key) {
|
|
3189
|
+
throw new Error("Missing required fields: namespace, key");
|
|
3190
|
+
}
|
|
3191
|
+
try {
|
|
3192
|
+
await deleteMemoriesByKeyOp(storageCtx, namespace, key);
|
|
3193
|
+
setMemories(
|
|
3194
|
+
(prev) => prev.filter((m) => !(m.namespace === namespace && m.key === key))
|
|
3195
|
+
);
|
|
3196
|
+
} catch (error) {
|
|
3197
|
+
throw new Error(
|
|
3198
|
+
`Failed to delete memories for "${namespace}:${key}": ` + (error instanceof Error ? error.message : String(error))
|
|
3199
|
+
);
|
|
3200
|
+
}
|
|
3201
|
+
},
|
|
3202
|
+
[storageCtx]
|
|
3203
|
+
);
|
|
3204
|
+
const clearMemories = useCallback5(async () => {
|
|
3205
|
+
try {
|
|
3206
|
+
await clearAllMemoriesOp(storageCtx);
|
|
3207
|
+
setMemories([]);
|
|
3208
|
+
} catch (error) {
|
|
3209
|
+
throw new Error(
|
|
3210
|
+
"Failed to clear all memories: " + (error instanceof Error ? error.message : String(error))
|
|
3211
|
+
);
|
|
3212
|
+
}
|
|
3213
|
+
}, [storageCtx]);
|
|
3214
|
+
return {
|
|
3215
|
+
memories,
|
|
3216
|
+
refreshMemories,
|
|
3217
|
+
extractMemoriesFromMessage,
|
|
3218
|
+
searchMemories,
|
|
3219
|
+
fetchAllMemories,
|
|
3220
|
+
fetchMemoriesByNamespace,
|
|
3221
|
+
fetchMemoriesByKey,
|
|
3222
|
+
getMemoryById,
|
|
3223
|
+
saveMemory,
|
|
3224
|
+
saveMemories,
|
|
3225
|
+
updateMemory,
|
|
3226
|
+
removeMemory,
|
|
3227
|
+
removeMemoryById,
|
|
3228
|
+
removeMemories,
|
|
3229
|
+
clearMemories
|
|
3230
|
+
};
|
|
3231
|
+
}
|
|
3232
|
+
|
|
3233
|
+
// src/lib/db/schema.ts
|
|
3234
|
+
import { appSchema as appSchema3, tableSchema as tableSchema3 } from "@nozbe/watermelondb";
|
|
3235
|
+
import {
|
|
3236
|
+
schemaMigrations as schemaMigrations2,
|
|
3237
|
+
addColumns as addColumns2,
|
|
3238
|
+
createTable
|
|
3239
|
+
} from "@nozbe/watermelondb/Schema/migrations";
|
|
3240
|
+
|
|
3241
|
+
// src/lib/db/settings/models.ts
|
|
3242
|
+
import { Model as Model3 } from "@nozbe/watermelondb";
|
|
3243
|
+
import { text as text3 } from "@nozbe/watermelondb/decorators";
|
|
3244
|
+
var ModelPreference = class extends Model3 {
|
|
3245
|
+
};
|
|
3246
|
+
ModelPreference.table = "modelPreferences";
|
|
3247
|
+
__decorateClass([
|
|
3248
|
+
text3("wallet_address")
|
|
3249
|
+
], ModelPreference.prototype, "walletAddress", 2);
|
|
3250
|
+
__decorateClass([
|
|
3251
|
+
text3("models")
|
|
3252
|
+
], ModelPreference.prototype, "models", 2);
|
|
3253
|
+
|
|
3254
|
+
// src/lib/db/schema.ts
|
|
3255
|
+
var SDK_SCHEMA_VERSION = 6;
|
|
3256
|
+
var sdkSchema = appSchema3({
|
|
3257
|
+
version: SDK_SCHEMA_VERSION,
|
|
3258
|
+
tables: [
|
|
3259
|
+
// Chat storage tables
|
|
3260
|
+
tableSchema3({
|
|
3261
|
+
name: "history",
|
|
3262
|
+
columns: [
|
|
3263
|
+
{ name: "message_id", type: "number" },
|
|
3264
|
+
{ name: "conversation_id", type: "string", isIndexed: true },
|
|
3265
|
+
{ name: "role", type: "string", isIndexed: true },
|
|
3266
|
+
{ name: "content", type: "string" },
|
|
3267
|
+
{ name: "model", type: "string", isOptional: true },
|
|
3268
|
+
{ name: "files", type: "string", isOptional: true },
|
|
3269
|
+
{ name: "created_at", type: "number", isIndexed: true },
|
|
3270
|
+
{ name: "updated_at", type: "number" },
|
|
3271
|
+
{ name: "vector", type: "string", isOptional: true },
|
|
3272
|
+
{ name: "embedding_model", type: "string", isOptional: true },
|
|
3273
|
+
{ name: "usage", type: "string", isOptional: true },
|
|
3274
|
+
{ name: "sources", type: "string", isOptional: true },
|
|
3275
|
+
{ name: "response_duration", type: "number", isOptional: true },
|
|
3276
|
+
{ name: "was_stopped", type: "boolean", isOptional: true },
|
|
3277
|
+
{ name: "error", type: "string", isOptional: true },
|
|
3278
|
+
{ name: "thought_process", type: "string", isOptional: true }
|
|
3279
|
+
// JSON stringified ActivityPhase[]
|
|
3280
|
+
]
|
|
3281
|
+
}),
|
|
3282
|
+
tableSchema3({
|
|
3283
|
+
name: "conversations",
|
|
3284
|
+
columns: [
|
|
3285
|
+
{ name: "conversation_id", type: "string", isIndexed: true },
|
|
3286
|
+
{ name: "title", type: "string" },
|
|
3287
|
+
{ name: "created_at", type: "number" },
|
|
3288
|
+
{ name: "updated_at", type: "number" },
|
|
3289
|
+
{ name: "is_deleted", type: "boolean", isIndexed: true }
|
|
3290
|
+
]
|
|
3291
|
+
}),
|
|
3292
|
+
// Memory storage tables
|
|
3293
|
+
tableSchema3({
|
|
3294
|
+
name: "memories",
|
|
3295
|
+
columns: [
|
|
3296
|
+
{ name: "type", type: "string", isIndexed: true },
|
|
3297
|
+
{ name: "namespace", type: "string", isIndexed: true },
|
|
3298
|
+
{ name: "key", type: "string", isIndexed: true },
|
|
3299
|
+
{ name: "value", type: "string" },
|
|
3300
|
+
{ name: "raw_evidence", type: "string" },
|
|
3301
|
+
{ name: "confidence", type: "number" },
|
|
3302
|
+
{ name: "pii", type: "boolean", isIndexed: true },
|
|
3303
|
+
{ name: "composite_key", type: "string", isIndexed: true },
|
|
3304
|
+
{ name: "unique_key", type: "string", isIndexed: true },
|
|
3305
|
+
{ name: "created_at", type: "number", isIndexed: true },
|
|
3306
|
+
{ name: "updated_at", type: "number" },
|
|
3307
|
+
{ name: "embedding", type: "string", isOptional: true },
|
|
3308
|
+
{ name: "embedding_model", type: "string", isOptional: true },
|
|
3309
|
+
{ name: "is_deleted", type: "boolean", isIndexed: true }
|
|
3310
|
+
]
|
|
3311
|
+
}),
|
|
3312
|
+
// Settings storage tables
|
|
3313
|
+
tableSchema3({
|
|
3314
|
+
name: "modelPreferences",
|
|
3315
|
+
columns: [
|
|
3316
|
+
{ name: "wallet_address", type: "string", isIndexed: true },
|
|
3317
|
+
{ name: "models", type: "string", isOptional: true }
|
|
3318
|
+
]
|
|
3319
|
+
})
|
|
3320
|
+
]
|
|
3321
|
+
});
|
|
3322
|
+
var sdkMigrations = schemaMigrations2({
|
|
3323
|
+
migrations: [
|
|
3324
|
+
// v2 -> v3: Added was_stopped column to history
|
|
3325
|
+
{
|
|
3326
|
+
toVersion: 3,
|
|
3327
|
+
steps: [
|
|
3328
|
+
addColumns2({
|
|
3329
|
+
table: "history",
|
|
3330
|
+
columns: [{ name: "was_stopped", type: "boolean", isOptional: true }]
|
|
3331
|
+
})
|
|
3332
|
+
]
|
|
3333
|
+
},
|
|
3334
|
+
// v3 -> v4: Added settings storage (modelPreferences table)
|
|
3335
|
+
{
|
|
3336
|
+
toVersion: 4,
|
|
3337
|
+
steps: [
|
|
3338
|
+
createTable({
|
|
3339
|
+
name: "modelPreferences",
|
|
3340
|
+
columns: [
|
|
3341
|
+
{ name: "wallet_address", type: "string", isIndexed: true },
|
|
3342
|
+
{ name: "models", type: "string", isOptional: true }
|
|
3343
|
+
]
|
|
3344
|
+
})
|
|
3345
|
+
]
|
|
3346
|
+
},
|
|
3347
|
+
// v4 -> v5: Added error column to history for error persistence
|
|
3348
|
+
{
|
|
3349
|
+
toVersion: 5,
|
|
3350
|
+
steps: [
|
|
3351
|
+
addColumns2({
|
|
3352
|
+
table: "history",
|
|
3353
|
+
columns: [{ name: "error", type: "string", isOptional: true }]
|
|
3354
|
+
})
|
|
3355
|
+
]
|
|
3356
|
+
},
|
|
3357
|
+
// v5 -> v6: Added thought_process column to history table
|
|
3358
|
+
{
|
|
3359
|
+
toVersion: 6,
|
|
3360
|
+
steps: [
|
|
3361
|
+
addColumns2({
|
|
3362
|
+
table: "history",
|
|
3363
|
+
columns: [
|
|
3364
|
+
{ name: "thought_process", type: "string", isOptional: true }
|
|
3365
|
+
]
|
|
3366
|
+
})
|
|
3367
|
+
]
|
|
3368
|
+
}
|
|
3369
|
+
]
|
|
3370
|
+
});
|
|
3371
|
+
var sdkModelClasses = [
|
|
3372
|
+
Message,
|
|
3373
|
+
Conversation,
|
|
3374
|
+
Memory,
|
|
3375
|
+
ModelPreference
|
|
3376
|
+
];
|
|
3377
|
+
export {
|
|
3378
|
+
Conversation as ChatConversation,
|
|
3379
|
+
Message as ChatMessage,
|
|
3380
|
+
Memory as StoredMemoryModel,
|
|
3381
|
+
chatStorageMigrations,
|
|
3382
|
+
chatStorageSchema,
|
|
3383
|
+
generateCompositeKey,
|
|
3384
|
+
generateConversationId,
|
|
3385
|
+
generateUniqueKey,
|
|
3386
|
+
memoryStorageSchema,
|
|
3387
|
+
sdkMigrations,
|
|
3388
|
+
sdkModelClasses,
|
|
3389
|
+
sdkSchema,
|
|
3390
|
+
useChat,
|
|
3391
|
+
useChatStorage,
|
|
3392
|
+
useImageGeneration,
|
|
3393
|
+
useMemoryStorage,
|
|
3394
|
+
useModels
|
|
3395
|
+
};
|