@usewhisper/sdk 2.2.0 → 3.0.0
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/index.d.mts +735 -11
- package/index.d.ts +735 -11
- package/index.js +2181 -121
- package/index.mjs +2165 -121
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -1,4 +1,377 @@
|
|
|
1
|
+
// ../src/sdk/core/telemetry.ts
|
|
2
|
+
var DiagnosticsStore = class {
|
|
3
|
+
maxEntries;
|
|
4
|
+
records = [];
|
|
5
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
6
|
+
constructor(maxEntries = 1e3) {
|
|
7
|
+
this.maxEntries = Math.max(1, maxEntries);
|
|
8
|
+
}
|
|
9
|
+
add(record) {
|
|
10
|
+
this.records.push(record);
|
|
11
|
+
if (this.records.length > this.maxEntries) {
|
|
12
|
+
this.records.splice(0, this.records.length - this.maxEntries);
|
|
13
|
+
}
|
|
14
|
+
for (const fn of this.subscribers) {
|
|
15
|
+
try {
|
|
16
|
+
fn(record);
|
|
17
|
+
} catch {
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
getLast(limit = 25) {
|
|
22
|
+
const count = Math.max(1, limit);
|
|
23
|
+
return this.records.slice(-count);
|
|
24
|
+
}
|
|
25
|
+
snapshot() {
|
|
26
|
+
const total = this.records.length;
|
|
27
|
+
const success = this.records.filter((r) => r.success).length;
|
|
28
|
+
const failure = total - success;
|
|
29
|
+
const duration = this.records.reduce((acc, item) => acc + item.durationMs, 0);
|
|
30
|
+
return {
|
|
31
|
+
total,
|
|
32
|
+
success,
|
|
33
|
+
failure,
|
|
34
|
+
avgDurationMs: total > 0 ? duration / total : 0,
|
|
35
|
+
lastTraceId: this.records[this.records.length - 1]?.traceId
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
subscribe(fn) {
|
|
39
|
+
this.subscribers.add(fn);
|
|
40
|
+
return () => {
|
|
41
|
+
this.subscribers.delete(fn);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// ../src/sdk/core/utils.ts
|
|
47
|
+
function normalizeBaseUrl(url) {
|
|
48
|
+
let normalized = url.trim().replace(/\/+$/, "");
|
|
49
|
+
normalized = normalized.replace(/\/api\/v1$/i, "");
|
|
50
|
+
normalized = normalized.replace(/\/v1$/i, "");
|
|
51
|
+
normalized = normalized.replace(/\/api$/i, "");
|
|
52
|
+
return normalized;
|
|
53
|
+
}
|
|
54
|
+
function normalizeEndpoint(endpoint) {
|
|
55
|
+
const withLeadingSlash = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
|
|
56
|
+
if (/^\/api\/v1(\/|$)/i.test(withLeadingSlash)) {
|
|
57
|
+
return withLeadingSlash.replace(/^\/api/i, "");
|
|
58
|
+
}
|
|
59
|
+
return withLeadingSlash;
|
|
60
|
+
}
|
|
61
|
+
function nowIso() {
|
|
62
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
63
|
+
}
|
|
64
|
+
function stableHash(input) {
|
|
65
|
+
let hash = 2166136261;
|
|
66
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
67
|
+
hash ^= input.charCodeAt(i);
|
|
68
|
+
hash = Math.imul(hash, 16777619);
|
|
69
|
+
}
|
|
70
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
71
|
+
}
|
|
72
|
+
function normalizeQuery(query) {
|
|
73
|
+
return query.trim().toLowerCase().replace(/\s+/g, " ");
|
|
74
|
+
}
|
|
75
|
+
function randomId(prefix = "id") {
|
|
76
|
+
return `${prefix}_${stableHash(`${Date.now()}_${Math.random()}`)}`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ../src/sdk/core/client.ts
|
|
80
|
+
var DEFAULT_TIMEOUTS = {
|
|
81
|
+
searchMs: 3e3,
|
|
82
|
+
writeAckMs: 2e3,
|
|
83
|
+
bulkMs: 1e4,
|
|
84
|
+
profileMs: 2500,
|
|
85
|
+
sessionMs: 2500
|
|
86
|
+
};
|
|
87
|
+
var DEFAULT_RETRYABLE_STATUS = [408, 429, 500, 502, 503, 504];
|
|
88
|
+
var DEFAULT_RETRY_ATTEMPTS = {
|
|
89
|
+
search: 3,
|
|
90
|
+
writeAck: 2,
|
|
91
|
+
bulk: 2,
|
|
92
|
+
profile: 2,
|
|
93
|
+
session: 2,
|
|
94
|
+
query: 3,
|
|
95
|
+
get: 2
|
|
96
|
+
};
|
|
97
|
+
function isObject(value) {
|
|
98
|
+
return typeof value === "object" && value !== null;
|
|
99
|
+
}
|
|
100
|
+
function toMessage(payload, status, statusText) {
|
|
101
|
+
if (typeof payload === "string" && payload.trim()) return payload;
|
|
102
|
+
if (isObject(payload)) {
|
|
103
|
+
const maybeError = payload.error;
|
|
104
|
+
const maybeMessage = payload.message;
|
|
105
|
+
if (typeof maybeError === "string" && maybeError.trim()) return maybeError;
|
|
106
|
+
if (typeof maybeMessage === "string" && maybeMessage.trim()) return maybeMessage;
|
|
107
|
+
if (isObject(maybeError) && typeof maybeError.message === "string") return maybeError.message;
|
|
108
|
+
}
|
|
109
|
+
return `HTTP ${status}: ${statusText}`;
|
|
110
|
+
}
|
|
111
|
+
var RuntimeClientError = class extends Error {
|
|
112
|
+
status;
|
|
113
|
+
retryable;
|
|
114
|
+
code;
|
|
115
|
+
details;
|
|
116
|
+
traceId;
|
|
117
|
+
constructor(args) {
|
|
118
|
+
super(args.message);
|
|
119
|
+
this.name = "RuntimeClientError";
|
|
120
|
+
this.status = args.status;
|
|
121
|
+
this.retryable = args.retryable;
|
|
122
|
+
this.code = args.code;
|
|
123
|
+
this.details = args.details;
|
|
124
|
+
this.traceId = args.traceId;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
var RuntimeClient = class {
|
|
128
|
+
apiKey;
|
|
129
|
+
baseUrl;
|
|
130
|
+
sdkVersion;
|
|
131
|
+
compatMode;
|
|
132
|
+
retryPolicy;
|
|
133
|
+
timeouts;
|
|
134
|
+
diagnostics;
|
|
135
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
136
|
+
constructor(options, diagnostics) {
|
|
137
|
+
if (!options.apiKey) {
|
|
138
|
+
throw new RuntimeClientError({
|
|
139
|
+
code: "INVALID_API_KEY",
|
|
140
|
+
message: "API key is required",
|
|
141
|
+
retryable: false
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
this.apiKey = options.apiKey;
|
|
145
|
+
this.baseUrl = normalizeBaseUrl(options.baseUrl || "https://context.usewhisper.dev");
|
|
146
|
+
this.sdkVersion = options.sdkVersion || "2.x-runtime";
|
|
147
|
+
this.compatMode = options.compatMode || "fallback";
|
|
148
|
+
this.retryPolicy = {
|
|
149
|
+
retryableStatusCodes: options.retryPolicy?.retryableStatusCodes || DEFAULT_RETRYABLE_STATUS,
|
|
150
|
+
retryOnNetworkError: options.retryPolicy?.retryOnNetworkError ?? true,
|
|
151
|
+
maxBackoffMs: options.retryPolicy?.maxBackoffMs ?? 1200,
|
|
152
|
+
baseBackoffMs: options.retryPolicy?.baseBackoffMs ?? 250,
|
|
153
|
+
maxAttemptsByOperation: options.retryPolicy?.maxAttemptsByOperation || {}
|
|
154
|
+
};
|
|
155
|
+
this.timeouts = {
|
|
156
|
+
...DEFAULT_TIMEOUTS,
|
|
157
|
+
...options.timeouts || {}
|
|
158
|
+
};
|
|
159
|
+
this.diagnostics = diagnostics || new DiagnosticsStore(1e3);
|
|
160
|
+
}
|
|
161
|
+
getDiagnosticsStore() {
|
|
162
|
+
return this.diagnostics;
|
|
163
|
+
}
|
|
164
|
+
getCompatMode() {
|
|
165
|
+
return this.compatMode;
|
|
166
|
+
}
|
|
167
|
+
timeoutFor(operation) {
|
|
168
|
+
switch (operation) {
|
|
169
|
+
case "search":
|
|
170
|
+
return this.timeouts.searchMs;
|
|
171
|
+
case "writeAck":
|
|
172
|
+
return this.timeouts.writeAckMs;
|
|
173
|
+
case "bulk":
|
|
174
|
+
return this.timeouts.bulkMs;
|
|
175
|
+
case "profile":
|
|
176
|
+
return this.timeouts.profileMs;
|
|
177
|
+
case "session":
|
|
178
|
+
return this.timeouts.sessionMs;
|
|
179
|
+
case "query":
|
|
180
|
+
case "get":
|
|
181
|
+
default:
|
|
182
|
+
return this.timeouts.searchMs;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
maxAttemptsFor(operation) {
|
|
186
|
+
const override = this.retryPolicy.maxAttemptsByOperation?.[operation];
|
|
187
|
+
return Math.max(1, override ?? DEFAULT_RETRY_ATTEMPTS[operation]);
|
|
188
|
+
}
|
|
189
|
+
shouldRetryStatus(status) {
|
|
190
|
+
return status !== void 0 && this.retryPolicy.retryableStatusCodes?.includes(status) === true;
|
|
191
|
+
}
|
|
192
|
+
backoff(attempt) {
|
|
193
|
+
const base = this.retryPolicy.baseBackoffMs ?? 250;
|
|
194
|
+
const max = this.retryPolicy.maxBackoffMs ?? 1200;
|
|
195
|
+
const jitter = 0.8 + Math.random() * 0.4;
|
|
196
|
+
return Math.min(max, Math.floor(base * Math.pow(2, attempt) * jitter));
|
|
197
|
+
}
|
|
198
|
+
runtimeName() {
|
|
199
|
+
const maybeWindow = globalThis.window;
|
|
200
|
+
return maybeWindow && typeof maybeWindow === "object" ? "browser" : "node";
|
|
201
|
+
}
|
|
202
|
+
createRequestFingerprint(options) {
|
|
203
|
+
const normalizedEndpoint = normalizeEndpoint(options.endpoint);
|
|
204
|
+
const authFingerprint = stableHash(this.apiKey.replace(/^Bearer\s+/i, ""));
|
|
205
|
+
const payload = JSON.stringify({
|
|
206
|
+
method: options.method || "GET",
|
|
207
|
+
endpoint: normalizedEndpoint,
|
|
208
|
+
body: options.body || null,
|
|
209
|
+
extra: options.dedupeKeyExtra || "",
|
|
210
|
+
authFingerprint
|
|
211
|
+
});
|
|
212
|
+
return stableHash(payload);
|
|
213
|
+
}
|
|
214
|
+
async request(options) {
|
|
215
|
+
const dedupeKey = options.idempotent ? this.createRequestFingerprint(options) : null;
|
|
216
|
+
if (dedupeKey) {
|
|
217
|
+
const inFlight = this.inFlight.get(dedupeKey);
|
|
218
|
+
if (inFlight) {
|
|
219
|
+
const data = await inFlight;
|
|
220
|
+
this.diagnostics.add({
|
|
221
|
+
id: randomId("diag"),
|
|
222
|
+
startedAt: nowIso(),
|
|
223
|
+
endedAt: nowIso(),
|
|
224
|
+
traceId: data.traceId,
|
|
225
|
+
spanId: randomId("span"),
|
|
226
|
+
operation: options.operation,
|
|
227
|
+
method: options.method || "GET",
|
|
228
|
+
endpoint: normalizeEndpoint(options.endpoint),
|
|
229
|
+
status: data.status,
|
|
230
|
+
durationMs: 0,
|
|
231
|
+
success: true,
|
|
232
|
+
deduped: true
|
|
233
|
+
});
|
|
234
|
+
const cloned = {
|
|
235
|
+
data: data.data,
|
|
236
|
+
status: data.status,
|
|
237
|
+
traceId: data.traceId
|
|
238
|
+
};
|
|
239
|
+
return cloned;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
const runner = this.performRequest(options).then((data) => {
|
|
243
|
+
if (dedupeKey) this.inFlight.delete(dedupeKey);
|
|
244
|
+
return data;
|
|
245
|
+
}).catch((error) => {
|
|
246
|
+
if (dedupeKey) this.inFlight.delete(dedupeKey);
|
|
247
|
+
throw error;
|
|
248
|
+
});
|
|
249
|
+
if (dedupeKey) {
|
|
250
|
+
this.inFlight.set(dedupeKey, runner);
|
|
251
|
+
}
|
|
252
|
+
return runner;
|
|
253
|
+
}
|
|
254
|
+
async performRequest(options) {
|
|
255
|
+
const method = options.method || "GET";
|
|
256
|
+
const normalizedEndpoint = normalizeEndpoint(options.endpoint);
|
|
257
|
+
const operation = options.operation;
|
|
258
|
+
const maxAttempts = this.maxAttemptsFor(operation);
|
|
259
|
+
const timeoutMs = this.timeoutFor(operation);
|
|
260
|
+
const traceId = options.traceId || randomId("trace");
|
|
261
|
+
let lastError = null;
|
|
262
|
+
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
263
|
+
const spanId = randomId("span");
|
|
264
|
+
const startedAt = Date.now();
|
|
265
|
+
const controller = new AbortController();
|
|
266
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
267
|
+
try {
|
|
268
|
+
const response = await fetch(`${this.baseUrl}${normalizedEndpoint}`, {
|
|
269
|
+
method,
|
|
270
|
+
signal: controller.signal,
|
|
271
|
+
keepalive: method !== "GET",
|
|
272
|
+
headers: {
|
|
273
|
+
"Content-Type": "application/json",
|
|
274
|
+
Authorization: this.apiKey.startsWith("Bearer ") ? this.apiKey : `Bearer ${this.apiKey}`,
|
|
275
|
+
"X-API-Key": this.apiKey.replace(/^Bearer\s+/i, ""),
|
|
276
|
+
"x-trace-id": traceId,
|
|
277
|
+
"x-span-id": spanId,
|
|
278
|
+
"x-sdk-version": this.sdkVersion,
|
|
279
|
+
"x-sdk-runtime": this.runtimeName(),
|
|
280
|
+
...options.headers || {}
|
|
281
|
+
},
|
|
282
|
+
body: method === "GET" || method === "DELETE" ? void 0 : JSON.stringify(options.body || {})
|
|
283
|
+
});
|
|
284
|
+
clearTimeout(timeout);
|
|
285
|
+
let payload = null;
|
|
286
|
+
try {
|
|
287
|
+
payload = await response.json();
|
|
288
|
+
} catch {
|
|
289
|
+
payload = await response.text().catch(() => "");
|
|
290
|
+
}
|
|
291
|
+
const durationMs = Date.now() - startedAt;
|
|
292
|
+
const record = {
|
|
293
|
+
id: randomId("diag"),
|
|
294
|
+
startedAt: new Date(startedAt).toISOString(),
|
|
295
|
+
endedAt: nowIso(),
|
|
296
|
+
traceId,
|
|
297
|
+
spanId,
|
|
298
|
+
operation,
|
|
299
|
+
method,
|
|
300
|
+
endpoint: normalizedEndpoint,
|
|
301
|
+
status: response.status,
|
|
302
|
+
durationMs,
|
|
303
|
+
success: response.ok
|
|
304
|
+
};
|
|
305
|
+
this.diagnostics.add(record);
|
|
306
|
+
if (response.ok) {
|
|
307
|
+
return {
|
|
308
|
+
data: payload,
|
|
309
|
+
status: response.status,
|
|
310
|
+
traceId
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
const message = toMessage(payload, response.status, response.statusText);
|
|
314
|
+
const retryable = this.shouldRetryStatus(response.status);
|
|
315
|
+
const error = new RuntimeClientError({
|
|
316
|
+
message,
|
|
317
|
+
status: response.status,
|
|
318
|
+
retryable,
|
|
319
|
+
code: response.status === 404 ? "NOT_FOUND" : "REQUEST_FAILED",
|
|
320
|
+
details: payload,
|
|
321
|
+
traceId
|
|
322
|
+
});
|
|
323
|
+
lastError = error;
|
|
324
|
+
if (!retryable || attempt === maxAttempts - 1) {
|
|
325
|
+
throw error;
|
|
326
|
+
}
|
|
327
|
+
} catch (error) {
|
|
328
|
+
clearTimeout(timeout);
|
|
329
|
+
const durationMs = Date.now() - startedAt;
|
|
330
|
+
const isAbort = isObject(error) && error.name === "AbortError";
|
|
331
|
+
const mapped = error instanceof RuntimeClientError ? error : new RuntimeClientError({
|
|
332
|
+
message: isAbort ? "Request timed out" : error instanceof Error ? error.message : "Network error",
|
|
333
|
+
retryable: this.retryPolicy.retryOnNetworkError ?? true,
|
|
334
|
+
code: isAbort ? "TIMEOUT" : "NETWORK_ERROR",
|
|
335
|
+
traceId
|
|
336
|
+
});
|
|
337
|
+
lastError = mapped;
|
|
338
|
+
this.diagnostics.add({
|
|
339
|
+
id: randomId("diag"),
|
|
340
|
+
startedAt: new Date(startedAt).toISOString(),
|
|
341
|
+
endedAt: nowIso(),
|
|
342
|
+
traceId,
|
|
343
|
+
spanId,
|
|
344
|
+
operation,
|
|
345
|
+
method,
|
|
346
|
+
endpoint: normalizedEndpoint,
|
|
347
|
+
durationMs,
|
|
348
|
+
success: false,
|
|
349
|
+
errorCode: mapped.code,
|
|
350
|
+
errorMessage: mapped.message
|
|
351
|
+
});
|
|
352
|
+
if (!mapped.retryable || attempt === maxAttempts - 1) {
|
|
353
|
+
throw mapped;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
await new Promise((resolve) => setTimeout(resolve, this.backoff(attempt)));
|
|
357
|
+
}
|
|
358
|
+
throw lastError || new RuntimeClientError({
|
|
359
|
+
message: "Request failed",
|
|
360
|
+
retryable: false,
|
|
361
|
+
code: "REQUEST_FAILED"
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
|
|
1
366
|
// ../src/sdk/whisper-agent.ts
|
|
367
|
+
var DEPRECATION_WARNINGS = /* @__PURE__ */ new Set();
|
|
368
|
+
function warnDeprecatedOnce(key, message) {
|
|
369
|
+
if (DEPRECATION_WARNINGS.has(key)) return;
|
|
370
|
+
DEPRECATION_WARNINGS.add(key);
|
|
371
|
+
if (typeof console !== "undefined" && typeof console.warn === "function") {
|
|
372
|
+
console.warn(message);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
2
375
|
var Whisper = class {
|
|
3
376
|
client;
|
|
4
377
|
options;
|
|
@@ -16,6 +389,10 @@ var Whisper = class {
|
|
|
16
389
|
if (options.timeoutMs) clientConfig.timeoutMs = options.timeoutMs;
|
|
17
390
|
if (options.retry) clientConfig.retry = options.retry;
|
|
18
391
|
this.client = new WhisperContext(clientConfig);
|
|
392
|
+
warnDeprecatedOnce(
|
|
393
|
+
"whisper_agent_wrapper",
|
|
394
|
+
"[Whisper SDK] Whisper wrapper is supported for v2 compatibility. Prefer WhisperClient for new integrations."
|
|
395
|
+
);
|
|
19
396
|
const finalRetry = options.retry || { maxAttempts: 3, baseDelayMs: 250, maxDelayMs: 2e3 };
|
|
20
397
|
this.options = {
|
|
21
398
|
apiKey: options.apiKey,
|
|
@@ -117,7 +494,7 @@ ${context}` : "",
|
|
|
117
494
|
if (extractedMemories.length > 0) {
|
|
118
495
|
const bulk = await this.client.addMemoriesBulk({
|
|
119
496
|
project: options?.project ?? this.options.project,
|
|
120
|
-
|
|
497
|
+
write_mode: "async",
|
|
121
498
|
memories: extractedMemories.map((m) => ({
|
|
122
499
|
content: m.content,
|
|
123
500
|
memory_type: m.memoryType,
|
|
@@ -185,6 +562,10 @@ ${context}` : "",
|
|
|
185
562
|
extracted: result?.memories_created ?? 0
|
|
186
563
|
};
|
|
187
564
|
} catch (error) {
|
|
565
|
+
const fallback = await this.fallbackCaptureViaAddMemory(messages, options);
|
|
566
|
+
if (fallback.success) {
|
|
567
|
+
return fallback;
|
|
568
|
+
}
|
|
188
569
|
console.error("[Whisper] Session capture failed:", error);
|
|
189
570
|
return { success: false, extracted: 0 };
|
|
190
571
|
}
|
|
@@ -237,13 +618,1092 @@ User: ${params.userMessage}` : params.userMessage;
|
|
|
237
618
|
if (bulkResponse?.memory?.id) {
|
|
238
619
|
ids.push(bulkResponse.memory.id);
|
|
239
620
|
}
|
|
240
|
-
if (bulkResponse?.id) {
|
|
241
|
-
ids.push(bulkResponse.id);
|
|
621
|
+
if (bulkResponse?.id) {
|
|
622
|
+
ids.push(bulkResponse.id);
|
|
623
|
+
}
|
|
624
|
+
return Array.from(new Set(ids));
|
|
625
|
+
}
|
|
626
|
+
async fallbackCaptureViaAddMemory(messages, options) {
|
|
627
|
+
const userMessages = messages.filter((m) => m.role === "user").map((m) => (m.content || "").trim()).filter((content) => content.length >= 5).slice(-2);
|
|
628
|
+
if (userMessages.length === 0) {
|
|
629
|
+
return { success: false, extracted: 0 };
|
|
630
|
+
}
|
|
631
|
+
let extracted = 0;
|
|
632
|
+
for (const content of userMessages) {
|
|
633
|
+
try {
|
|
634
|
+
await this.client.addMemory({
|
|
635
|
+
project: options?.project ?? this.options.project,
|
|
636
|
+
content,
|
|
637
|
+
memory_type: "factual",
|
|
638
|
+
user_id: options?.userId ?? this.userId,
|
|
639
|
+
session_id: options?.sessionId ?? this.sessionId,
|
|
640
|
+
allow_legacy_fallback: true
|
|
641
|
+
});
|
|
642
|
+
extracted += 1;
|
|
643
|
+
} catch {
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
return { success: extracted > 0, extracted };
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
var whisper_agent_default = Whisper;
|
|
650
|
+
|
|
651
|
+
// ../src/sdk/core/cache.ts
|
|
652
|
+
var SearchResponseCache = class {
|
|
653
|
+
ttlMs;
|
|
654
|
+
capacity;
|
|
655
|
+
byKey = /* @__PURE__ */ new Map();
|
|
656
|
+
scopeIndex = /* @__PURE__ */ new Map();
|
|
657
|
+
constructor(ttlMs = 7e3, capacity = 500) {
|
|
658
|
+
this.ttlMs = Math.max(1e3, ttlMs);
|
|
659
|
+
this.capacity = Math.max(10, capacity);
|
|
660
|
+
}
|
|
661
|
+
makeScopeKey(project, userId, sessionId) {
|
|
662
|
+
return `${project}:${userId || "_"}:${sessionId || "_"}`;
|
|
663
|
+
}
|
|
664
|
+
makeKey(input) {
|
|
665
|
+
const normalized = {
|
|
666
|
+
project: input.project,
|
|
667
|
+
userId: input.userId || "",
|
|
668
|
+
sessionId: input.sessionId || "",
|
|
669
|
+
query: normalizeQuery(input.query),
|
|
670
|
+
topK: input.topK,
|
|
671
|
+
profile: input.profile,
|
|
672
|
+
includePending: input.includePending
|
|
673
|
+
};
|
|
674
|
+
return `search:${stableHash(JSON.stringify(normalized))}`;
|
|
675
|
+
}
|
|
676
|
+
get(key) {
|
|
677
|
+
const found = this.byKey.get(key);
|
|
678
|
+
if (!found) return null;
|
|
679
|
+
if (found.expiresAt <= Date.now()) {
|
|
680
|
+
this.deleteByKey(key);
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
found.touchedAt = Date.now();
|
|
684
|
+
return found.value;
|
|
685
|
+
}
|
|
686
|
+
set(key, scopeKey, value) {
|
|
687
|
+
this.byKey.set(key, {
|
|
688
|
+
value,
|
|
689
|
+
scopeKey,
|
|
690
|
+
touchedAt: Date.now(),
|
|
691
|
+
expiresAt: Date.now() + this.ttlMs
|
|
692
|
+
});
|
|
693
|
+
if (!this.scopeIndex.has(scopeKey)) {
|
|
694
|
+
this.scopeIndex.set(scopeKey, /* @__PURE__ */ new Set());
|
|
695
|
+
}
|
|
696
|
+
this.scopeIndex.get(scopeKey).add(key);
|
|
697
|
+
this.evictIfNeeded();
|
|
698
|
+
}
|
|
699
|
+
invalidateScope(scopeKey) {
|
|
700
|
+
const keys = this.scopeIndex.get(scopeKey);
|
|
701
|
+
if (!keys || keys.size === 0) {
|
|
702
|
+
return 0;
|
|
703
|
+
}
|
|
704
|
+
const toDelete = Array.from(keys);
|
|
705
|
+
for (const key of toDelete) {
|
|
706
|
+
this.deleteByKey(key);
|
|
707
|
+
}
|
|
708
|
+
this.scopeIndex.delete(scopeKey);
|
|
709
|
+
return toDelete.length;
|
|
710
|
+
}
|
|
711
|
+
evictIfNeeded() {
|
|
712
|
+
if (this.byKey.size <= this.capacity) return;
|
|
713
|
+
const ordered = Array.from(this.byKey.entries()).sort((a, b) => a[1].touchedAt - b[1].touchedAt);
|
|
714
|
+
const removeCount = this.byKey.size - this.capacity;
|
|
715
|
+
for (let i = 0; i < removeCount; i += 1) {
|
|
716
|
+
this.deleteByKey(ordered[i][0]);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
deleteByKey(key) {
|
|
720
|
+
const found = this.byKey.get(key);
|
|
721
|
+
if (!found) return;
|
|
722
|
+
this.byKey.delete(key);
|
|
723
|
+
const scopeKeys = this.scopeIndex.get(found.scopeKey);
|
|
724
|
+
if (!scopeKeys) return;
|
|
725
|
+
scopeKeys.delete(key);
|
|
726
|
+
if (scopeKeys.size === 0) {
|
|
727
|
+
this.scopeIndex.delete(found.scopeKey);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
// ../src/sdk/core/queue.ts
|
|
733
|
+
var InMemoryQueueStore = class {
|
|
734
|
+
items = [];
|
|
735
|
+
async load() {
|
|
736
|
+
return [...this.items];
|
|
737
|
+
}
|
|
738
|
+
async save(items) {
|
|
739
|
+
this.items = [...items];
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
var WriteQueue = class {
|
|
743
|
+
flushHandler;
|
|
744
|
+
store;
|
|
745
|
+
maxBatchSize;
|
|
746
|
+
flushIntervalMs;
|
|
747
|
+
maxAttempts;
|
|
748
|
+
queue = [];
|
|
749
|
+
flushTimer = null;
|
|
750
|
+
flushing = false;
|
|
751
|
+
lastFlushAt;
|
|
752
|
+
lastFlushCount = 0;
|
|
753
|
+
constructor(args) {
|
|
754
|
+
this.flushHandler = args.flushHandler;
|
|
755
|
+
this.store = args.store || new InMemoryQueueStore();
|
|
756
|
+
this.maxBatchSize = Math.max(1, args.maxBatchSize ?? 50);
|
|
757
|
+
this.flushIntervalMs = Math.max(10, args.flushIntervalMs ?? 100);
|
|
758
|
+
this.maxAttempts = Math.max(1, args.maxAttempts ?? 2);
|
|
759
|
+
}
|
|
760
|
+
async start() {
|
|
761
|
+
const pending = await this.store.load();
|
|
762
|
+
if (pending.length > 0) {
|
|
763
|
+
this.queue.push(...pending);
|
|
764
|
+
}
|
|
765
|
+
if (!this.flushTimer) {
|
|
766
|
+
this.flushTimer = setInterval(() => {
|
|
767
|
+
void this.flush();
|
|
768
|
+
}, this.flushIntervalMs);
|
|
769
|
+
const timer = this.flushTimer;
|
|
770
|
+
if (typeof timer.unref === "function") {
|
|
771
|
+
timer.unref();
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
this.bindProcessHooks();
|
|
775
|
+
}
|
|
776
|
+
async stop() {
|
|
777
|
+
if (this.flushTimer) {
|
|
778
|
+
clearInterval(this.flushTimer);
|
|
779
|
+
this.flushTimer = null;
|
|
780
|
+
}
|
|
781
|
+
await this.flush();
|
|
782
|
+
}
|
|
783
|
+
status() {
|
|
784
|
+
return {
|
|
785
|
+
queued: this.queue.length,
|
|
786
|
+
flushing: this.flushing,
|
|
787
|
+
lastFlushAt: this.lastFlushAt,
|
|
788
|
+
lastFlushCount: this.lastFlushCount
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
async enqueue(input) {
|
|
792
|
+
const eventId = input.eventId || this.makeEventId(input);
|
|
793
|
+
const item = {
|
|
794
|
+
...input,
|
|
795
|
+
eventId,
|
|
796
|
+
createdAt: nowIso()
|
|
797
|
+
};
|
|
798
|
+
this.queue.push(item);
|
|
799
|
+
await this.store.save(this.queue);
|
|
800
|
+
if (this.queue.length >= this.maxBatchSize) {
|
|
801
|
+
void this.flush();
|
|
802
|
+
}
|
|
803
|
+
return item;
|
|
804
|
+
}
|
|
805
|
+
async flush() {
|
|
806
|
+
if (this.flushing || this.queue.length === 0) return;
|
|
807
|
+
this.flushing = true;
|
|
808
|
+
try {
|
|
809
|
+
while (this.queue.length > 0) {
|
|
810
|
+
const batch = this.queue.slice(0, this.maxBatchSize);
|
|
811
|
+
let done = false;
|
|
812
|
+
let error = null;
|
|
813
|
+
for (let attempt = 0; attempt < this.maxAttempts; attempt += 1) {
|
|
814
|
+
try {
|
|
815
|
+
await this.flushHandler(batch);
|
|
816
|
+
done = true;
|
|
817
|
+
break;
|
|
818
|
+
} catch (err) {
|
|
819
|
+
error = err;
|
|
820
|
+
await new Promise((resolve) => setTimeout(resolve, (attempt + 1) * 180));
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
if (!done) {
|
|
824
|
+
throw error instanceof Error ? error : new Error("Queue flush failed");
|
|
825
|
+
}
|
|
826
|
+
this.queue.splice(0, batch.length);
|
|
827
|
+
this.lastFlushAt = nowIso();
|
|
828
|
+
this.lastFlushCount = batch.length;
|
|
829
|
+
await this.store.save(this.queue);
|
|
830
|
+
}
|
|
831
|
+
} finally {
|
|
832
|
+
this.flushing = false;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
makeEventId(input) {
|
|
836
|
+
const source = JSON.stringify({
|
|
837
|
+
project: input.project,
|
|
838
|
+
userId: input.userId || "",
|
|
839
|
+
sessionId: input.sessionId || "",
|
|
840
|
+
payload: input.payload
|
|
841
|
+
});
|
|
842
|
+
return `evt_${stableHash(source)}`;
|
|
843
|
+
}
|
|
844
|
+
bindProcessHooks() {
|
|
845
|
+
if (typeof process === "undefined") return;
|
|
846
|
+
const proc = process;
|
|
847
|
+
const flushOnExit = () => {
|
|
848
|
+
void this.flush();
|
|
849
|
+
};
|
|
850
|
+
proc.once("beforeExit", flushOnExit);
|
|
851
|
+
proc.once("SIGINT", flushOnExit);
|
|
852
|
+
proc.once("SIGTERM", flushOnExit);
|
|
853
|
+
}
|
|
854
|
+
};
|
|
855
|
+
function createStorageQueueStore(key = "whisper_sdk_queue") {
|
|
856
|
+
const getStorage = () => {
|
|
857
|
+
const maybeStorage = globalThis.localStorage;
|
|
858
|
+
if (!maybeStorage || typeof maybeStorage !== "object") return null;
|
|
859
|
+
const candidate = maybeStorage;
|
|
860
|
+
if (typeof candidate.getItem !== "function" || typeof candidate.setItem !== "function") {
|
|
861
|
+
return null;
|
|
862
|
+
}
|
|
863
|
+
return {
|
|
864
|
+
getItem: candidate.getItem,
|
|
865
|
+
setItem: candidate.setItem
|
|
866
|
+
};
|
|
867
|
+
};
|
|
868
|
+
return {
|
|
869
|
+
async load() {
|
|
870
|
+
const storage = getStorage();
|
|
871
|
+
if (!storage) return [];
|
|
872
|
+
const raw = storage.getItem(key);
|
|
873
|
+
if (!raw) return [];
|
|
874
|
+
try {
|
|
875
|
+
const parsed = JSON.parse(raw);
|
|
876
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
877
|
+
} catch {
|
|
878
|
+
return [];
|
|
879
|
+
}
|
|
880
|
+
},
|
|
881
|
+
async save(items) {
|
|
882
|
+
const storage = getStorage();
|
|
883
|
+
if (!storage) return;
|
|
884
|
+
storage.setItem(key, JSON.stringify(items));
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
function createFileQueueStore(filePath) {
|
|
889
|
+
return {
|
|
890
|
+
async load() {
|
|
891
|
+
if (typeof process === "undefined") return [];
|
|
892
|
+
const fs = await import("fs/promises");
|
|
893
|
+
try {
|
|
894
|
+
const raw = await fs.readFile(filePath, "utf8");
|
|
895
|
+
const parsed = JSON.parse(raw);
|
|
896
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
897
|
+
} catch (error) {
|
|
898
|
+
const nodeError = error;
|
|
899
|
+
if (nodeError?.code === "ENOENT") {
|
|
900
|
+
return [];
|
|
901
|
+
}
|
|
902
|
+
return [];
|
|
903
|
+
}
|
|
904
|
+
},
|
|
905
|
+
async save(items) {
|
|
906
|
+
if (typeof process === "undefined") return;
|
|
907
|
+
const fs = await import("fs/promises");
|
|
908
|
+
const path = await import("path");
|
|
909
|
+
const dir = path.dirname(filePath);
|
|
910
|
+
await fs.mkdir(dir, { recursive: true });
|
|
911
|
+
await fs.writeFile(filePath, JSON.stringify(items), "utf8");
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// ../src/sdk/modules/memory.ts
|
|
917
|
+
function isEndpointNotFound(error) {
|
|
918
|
+
return error instanceof RuntimeClientError && error.status === 404;
|
|
919
|
+
}
|
|
920
|
+
function toSotaType(memoryType) {
|
|
921
|
+
if (!memoryType) return void 0;
|
|
922
|
+
switch (memoryType) {
|
|
923
|
+
case "episodic":
|
|
924
|
+
return "event";
|
|
925
|
+
case "semantic":
|
|
926
|
+
return "factual";
|
|
927
|
+
case "procedural":
|
|
928
|
+
return "instruction";
|
|
929
|
+
default:
|
|
930
|
+
return memoryType;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
function toLegacyType(memoryType) {
|
|
934
|
+
if (!memoryType) return void 0;
|
|
935
|
+
switch (memoryType) {
|
|
936
|
+
case "event":
|
|
937
|
+
return "episodic";
|
|
938
|
+
case "instruction":
|
|
939
|
+
return "procedural";
|
|
940
|
+
case "preference":
|
|
941
|
+
case "relationship":
|
|
942
|
+
case "opinion":
|
|
943
|
+
case "goal":
|
|
944
|
+
return "semantic";
|
|
945
|
+
default:
|
|
946
|
+
return memoryType;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
var MemoryModule = class {
|
|
950
|
+
constructor(client, cache, queue, options = {}) {
|
|
951
|
+
this.client = client;
|
|
952
|
+
this.cache = cache;
|
|
953
|
+
this.queue = queue;
|
|
954
|
+
this.options = options;
|
|
955
|
+
}
|
|
956
|
+
resolveProject(project) {
|
|
957
|
+
const value = project || this.options.defaultProject;
|
|
958
|
+
if (!value) {
|
|
959
|
+
throw new RuntimeClientError({
|
|
960
|
+
code: "MISSING_PROJECT",
|
|
961
|
+
message: "Project is required",
|
|
962
|
+
retryable: false
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
return value;
|
|
966
|
+
}
|
|
967
|
+
invalidate(project, userId, sessionId) {
|
|
968
|
+
if (this.options.cacheEnabled === false) {
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
971
|
+
const scope = this.cache.makeScopeKey(project, userId, sessionId);
|
|
972
|
+
this.cache.invalidateScope(scope);
|
|
973
|
+
}
|
|
974
|
+
async add(params) {
|
|
975
|
+
const project = this.resolveProject(params.project);
|
|
976
|
+
const queueEnabled = this.options.queueEnabled !== false;
|
|
977
|
+
const useQueue = queueEnabled && params.write_mode !== "sync" && params.async !== false;
|
|
978
|
+
if (useQueue) {
|
|
979
|
+
const queued = await this.queue.enqueue({
|
|
980
|
+
project,
|
|
981
|
+
userId: params.user_id,
|
|
982
|
+
sessionId: params.session_id,
|
|
983
|
+
payload: {
|
|
984
|
+
content: params.content,
|
|
985
|
+
memory_type: toSotaType(params.memory_type),
|
|
986
|
+
user_id: params.user_id,
|
|
987
|
+
session_id: params.session_id,
|
|
988
|
+
agent_id: params.agent_id,
|
|
989
|
+
importance: params.importance,
|
|
990
|
+
confidence: params.confidence,
|
|
991
|
+
metadata: params.metadata,
|
|
992
|
+
document_date: params.document_date,
|
|
993
|
+
event_date: params.event_date
|
|
994
|
+
}
|
|
995
|
+
});
|
|
996
|
+
this.invalidate(project, params.user_id, params.session_id);
|
|
997
|
+
return {
|
|
998
|
+
success: true,
|
|
999
|
+
mode: "async",
|
|
1000
|
+
queued: true,
|
|
1001
|
+
event_id: queued.eventId,
|
|
1002
|
+
accepted_at: queued.createdAt
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
try {
|
|
1006
|
+
const response = await this.client.request({
|
|
1007
|
+
endpoint: "/v1/memory",
|
|
1008
|
+
method: "POST",
|
|
1009
|
+
operation: "writeAck",
|
|
1010
|
+
body: {
|
|
1011
|
+
project,
|
|
1012
|
+
content: params.content,
|
|
1013
|
+
memory_type: toSotaType(params.memory_type),
|
|
1014
|
+
user_id: params.user_id,
|
|
1015
|
+
session_id: params.session_id,
|
|
1016
|
+
agent_id: params.agent_id,
|
|
1017
|
+
importance: params.importance,
|
|
1018
|
+
confidence: params.confidence,
|
|
1019
|
+
metadata: params.metadata,
|
|
1020
|
+
document_date: params.document_date,
|
|
1021
|
+
event_date: params.event_date,
|
|
1022
|
+
write_mode: "sync"
|
|
1023
|
+
}
|
|
1024
|
+
});
|
|
1025
|
+
this.invalidate(project, params.user_id, params.session_id);
|
|
1026
|
+
return {
|
|
1027
|
+
success: true,
|
|
1028
|
+
mode: "sync",
|
|
1029
|
+
trace_id: response.traceId
|
|
1030
|
+
};
|
|
1031
|
+
} catch (error) {
|
|
1032
|
+
if (this.client.getCompatMode() !== "fallback" || !isEndpointNotFound(error)) {
|
|
1033
|
+
throw error;
|
|
1034
|
+
}
|
|
1035
|
+
await this.client.request({
|
|
1036
|
+
endpoint: "/v1/memories",
|
|
1037
|
+
method: "POST",
|
|
1038
|
+
operation: "writeAck",
|
|
1039
|
+
body: {
|
|
1040
|
+
project,
|
|
1041
|
+
content: params.content,
|
|
1042
|
+
memory_type: toLegacyType(params.memory_type),
|
|
1043
|
+
user_id: params.user_id,
|
|
1044
|
+
session_id: params.session_id,
|
|
1045
|
+
agent_id: params.agent_id,
|
|
1046
|
+
importance: params.importance,
|
|
1047
|
+
metadata: params.metadata
|
|
1048
|
+
}
|
|
1049
|
+
});
|
|
1050
|
+
this.invalidate(project, params.user_id, params.session_id);
|
|
1051
|
+
return {
|
|
1052
|
+
success: true,
|
|
1053
|
+
mode: "sync"
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
async addBulk(params) {
|
|
1058
|
+
const project = this.resolveProject(params.project);
|
|
1059
|
+
if (!Array.isArray(params.memories) || params.memories.length === 0) {
|
|
1060
|
+
throw new RuntimeClientError({
|
|
1061
|
+
code: "VALIDATION_ERROR",
|
|
1062
|
+
message: "memories is required",
|
|
1063
|
+
retryable: false
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
const queueEnabled = this.options.queueEnabled !== false;
|
|
1067
|
+
const useQueue = queueEnabled && params.write_mode !== "sync" && params.async !== false;
|
|
1068
|
+
if (useQueue) {
|
|
1069
|
+
const queued = await Promise.all(
|
|
1070
|
+
params.memories.map(
|
|
1071
|
+
(memory) => this.queue.enqueue({
|
|
1072
|
+
project,
|
|
1073
|
+
userId: memory.user_id,
|
|
1074
|
+
sessionId: memory.session_id,
|
|
1075
|
+
payload: {
|
|
1076
|
+
content: memory.content,
|
|
1077
|
+
memory_type: toSotaType(memory.memory_type),
|
|
1078
|
+
user_id: memory.user_id,
|
|
1079
|
+
session_id: memory.session_id,
|
|
1080
|
+
agent_id: memory.agent_id,
|
|
1081
|
+
importance: memory.importance,
|
|
1082
|
+
confidence: memory.confidence,
|
|
1083
|
+
metadata: memory.metadata,
|
|
1084
|
+
document_date: memory.document_date,
|
|
1085
|
+
event_date: memory.event_date
|
|
1086
|
+
}
|
|
1087
|
+
})
|
|
1088
|
+
)
|
|
1089
|
+
);
|
|
1090
|
+
for (const memory of params.memories) {
|
|
1091
|
+
this.invalidate(project, memory.user_id, memory.session_id);
|
|
1092
|
+
}
|
|
1093
|
+
return {
|
|
1094
|
+
success: true,
|
|
1095
|
+
mode: "async",
|
|
1096
|
+
queued: true,
|
|
1097
|
+
created: queued.length
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
try {
|
|
1101
|
+
const response = await this.client.request({
|
|
1102
|
+
endpoint: "/v1/memory/bulk",
|
|
1103
|
+
method: "POST",
|
|
1104
|
+
operation: "bulk",
|
|
1105
|
+
body: {
|
|
1106
|
+
project,
|
|
1107
|
+
memories: params.memories.map((memory) => ({
|
|
1108
|
+
...memory,
|
|
1109
|
+
memory_type: toSotaType(memory.memory_type)
|
|
1110
|
+
})),
|
|
1111
|
+
write_mode: "sync"
|
|
1112
|
+
}
|
|
1113
|
+
});
|
|
1114
|
+
for (const memory of params.memories) {
|
|
1115
|
+
this.invalidate(project, memory.user_id, memory.session_id);
|
|
1116
|
+
}
|
|
1117
|
+
return {
|
|
1118
|
+
success: true,
|
|
1119
|
+
mode: "sync",
|
|
1120
|
+
trace_id: response.traceId
|
|
1121
|
+
};
|
|
1122
|
+
} catch (error) {
|
|
1123
|
+
if (this.client.getCompatMode() !== "fallback" || !isEndpointNotFound(error)) {
|
|
1124
|
+
throw error;
|
|
1125
|
+
}
|
|
1126
|
+
await Promise.all(
|
|
1127
|
+
params.memories.map(
|
|
1128
|
+
(memory) => this.add({
|
|
1129
|
+
project,
|
|
1130
|
+
...memory,
|
|
1131
|
+
write_mode: "sync"
|
|
1132
|
+
})
|
|
1133
|
+
)
|
|
1134
|
+
);
|
|
1135
|
+
return {
|
|
1136
|
+
success: true,
|
|
1137
|
+
mode: "sync"
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
async search(params) {
|
|
1142
|
+
const project = this.resolveProject(params.project);
|
|
1143
|
+
const topK = params.top_k || 10;
|
|
1144
|
+
const profile = params.profile || "fast";
|
|
1145
|
+
const includePending = params.include_pending !== false;
|
|
1146
|
+
const cacheKey = this.cache.makeKey({
|
|
1147
|
+
project,
|
|
1148
|
+
userId: params.user_id,
|
|
1149
|
+
sessionId: params.session_id,
|
|
1150
|
+
query: params.query,
|
|
1151
|
+
topK,
|
|
1152
|
+
profile,
|
|
1153
|
+
includePending
|
|
1154
|
+
});
|
|
1155
|
+
if (this.options.cacheEnabled !== false) {
|
|
1156
|
+
const cached = this.cache.get(cacheKey);
|
|
1157
|
+
if (cached) {
|
|
1158
|
+
return {
|
|
1159
|
+
...cached,
|
|
1160
|
+
cache_hit: true
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
try {
|
|
1165
|
+
const response = await this.client.request({
|
|
1166
|
+
endpoint: "/v1/memory/search",
|
|
1167
|
+
method: "POST",
|
|
1168
|
+
operation: "search",
|
|
1169
|
+
idempotent: true,
|
|
1170
|
+
body: {
|
|
1171
|
+
project,
|
|
1172
|
+
query: params.query,
|
|
1173
|
+
user_id: params.user_id,
|
|
1174
|
+
session_id: params.session_id,
|
|
1175
|
+
top_k: topK,
|
|
1176
|
+
profile,
|
|
1177
|
+
include_pending: includePending,
|
|
1178
|
+
memory_types: params.memory_type ? [toSotaType(params.memory_type)] : void 0
|
|
1179
|
+
}
|
|
1180
|
+
});
|
|
1181
|
+
const data = {
|
|
1182
|
+
...response.data || {},
|
|
1183
|
+
cache_hit: false
|
|
1184
|
+
};
|
|
1185
|
+
if (this.options.cacheEnabled !== false) {
|
|
1186
|
+
const scope = this.cache.makeScopeKey(project, params.user_id, params.session_id);
|
|
1187
|
+
this.cache.set(cacheKey, scope, data);
|
|
1188
|
+
}
|
|
1189
|
+
return data;
|
|
1190
|
+
} catch (error) {
|
|
1191
|
+
if (this.client.getCompatMode() !== "fallback" || !isEndpointNotFound(error)) {
|
|
1192
|
+
throw error;
|
|
1193
|
+
}
|
|
1194
|
+
const legacy = await this.client.request({
|
|
1195
|
+
endpoint: "/v1/memories/search",
|
|
1196
|
+
method: "POST",
|
|
1197
|
+
operation: "search",
|
|
1198
|
+
idempotent: true,
|
|
1199
|
+
body: {
|
|
1200
|
+
project,
|
|
1201
|
+
query: params.query,
|
|
1202
|
+
user_id: params.user_id,
|
|
1203
|
+
session_id: params.session_id,
|
|
1204
|
+
top_k: topK,
|
|
1205
|
+
memory_type: toLegacyType(params.memory_type)
|
|
1206
|
+
}
|
|
1207
|
+
});
|
|
1208
|
+
const data = {
|
|
1209
|
+
...legacy.data || {},
|
|
1210
|
+
cache_hit: false
|
|
1211
|
+
};
|
|
1212
|
+
if (this.options.cacheEnabled !== false) {
|
|
1213
|
+
const scope = this.cache.makeScopeKey(project, params.user_id, params.session_id);
|
|
1214
|
+
this.cache.set(cacheKey, scope, data);
|
|
1215
|
+
}
|
|
1216
|
+
return data;
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
async getUserProfile(params) {
|
|
1220
|
+
const project = this.resolveProject(params.project);
|
|
1221
|
+
const query = new URLSearchParams({
|
|
1222
|
+
project,
|
|
1223
|
+
...params.include_pending !== void 0 ? { include_pending: String(params.include_pending) } : {},
|
|
1224
|
+
...params.memory_types ? { memory_types: params.memory_types } : {}
|
|
1225
|
+
});
|
|
1226
|
+
try {
|
|
1227
|
+
const response = await this.client.request({
|
|
1228
|
+
endpoint: `/v1/memory/profile/${params.user_id}?${query}`,
|
|
1229
|
+
method: "GET",
|
|
1230
|
+
operation: "profile",
|
|
1231
|
+
idempotent: true
|
|
1232
|
+
});
|
|
1233
|
+
return response.data;
|
|
1234
|
+
} catch (error) {
|
|
1235
|
+
if (this.client.getCompatMode() !== "fallback" || !isEndpointNotFound(error)) {
|
|
1236
|
+
throw error;
|
|
1237
|
+
}
|
|
1238
|
+
const legacyQuery = new URLSearchParams({
|
|
1239
|
+
project,
|
|
1240
|
+
user_id: params.user_id,
|
|
1241
|
+
limit: "200"
|
|
1242
|
+
});
|
|
1243
|
+
const legacy = await this.client.request({
|
|
1244
|
+
endpoint: `/v1/memories?${legacyQuery}`,
|
|
1245
|
+
method: "GET",
|
|
1246
|
+
operation: "profile",
|
|
1247
|
+
idempotent: true
|
|
1248
|
+
});
|
|
1249
|
+
const memories = Array.isArray(legacy.data?.memories) ? legacy.data.memories : [];
|
|
1250
|
+
return {
|
|
1251
|
+
user_id: params.user_id,
|
|
1252
|
+
memories,
|
|
1253
|
+
count: memories.length
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
async getSessionMemories(params) {
|
|
1258
|
+
const project = this.resolveProject(params.project);
|
|
1259
|
+
const query = new URLSearchParams({
|
|
1260
|
+
project,
|
|
1261
|
+
...params.limit ? { limit: String(params.limit) } : {},
|
|
1262
|
+
...params.include_pending !== void 0 ? { include_pending: String(params.include_pending) } : {}
|
|
1263
|
+
});
|
|
1264
|
+
const response = await this.client.request({
|
|
1265
|
+
endpoint: `/v1/memory/session/${params.session_id}?${query}`,
|
|
1266
|
+
method: "GET",
|
|
1267
|
+
operation: "profile",
|
|
1268
|
+
idempotent: true
|
|
1269
|
+
});
|
|
1270
|
+
return response.data;
|
|
1271
|
+
}
|
|
1272
|
+
async update(memoryId, params) {
|
|
1273
|
+
try {
|
|
1274
|
+
await this.client.request({
|
|
1275
|
+
endpoint: `/v1/memory/${memoryId}`,
|
|
1276
|
+
method: "PUT",
|
|
1277
|
+
operation: "writeAck",
|
|
1278
|
+
body: params
|
|
1279
|
+
});
|
|
1280
|
+
return { success: true };
|
|
1281
|
+
} catch (error) {
|
|
1282
|
+
if (this.client.getCompatMode() !== "fallback" || !isEndpointNotFound(error)) {
|
|
1283
|
+
throw error;
|
|
1284
|
+
}
|
|
1285
|
+
await this.client.request({
|
|
1286
|
+
endpoint: `/v1/memories/${memoryId}`,
|
|
1287
|
+
method: "PUT",
|
|
1288
|
+
operation: "writeAck",
|
|
1289
|
+
body: { content: params.content }
|
|
1290
|
+
});
|
|
1291
|
+
return { success: true };
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
async delete(memoryId) {
|
|
1295
|
+
try {
|
|
1296
|
+
await this.client.request({
|
|
1297
|
+
endpoint: `/v1/memory/${memoryId}`,
|
|
1298
|
+
method: "DELETE",
|
|
1299
|
+
operation: "writeAck"
|
|
1300
|
+
});
|
|
1301
|
+
return { success: true, deleted: memoryId };
|
|
1302
|
+
} catch (error) {
|
|
1303
|
+
if (this.client.getCompatMode() !== "fallback" || !isEndpointNotFound(error)) {
|
|
1304
|
+
throw error;
|
|
1305
|
+
}
|
|
1306
|
+
await this.client.request({
|
|
1307
|
+
endpoint: `/v1/memories/${memoryId}`,
|
|
1308
|
+
method: "DELETE",
|
|
1309
|
+
operation: "writeAck"
|
|
1310
|
+
});
|
|
1311
|
+
return { success: true, deleted: memoryId };
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
async flag(params) {
|
|
1315
|
+
try {
|
|
1316
|
+
await this.client.request({
|
|
1317
|
+
endpoint: `/v1/memory/${params.memoryId}/flag`,
|
|
1318
|
+
method: "POST",
|
|
1319
|
+
operation: "writeAck",
|
|
1320
|
+
body: {
|
|
1321
|
+
reason: params.reason,
|
|
1322
|
+
severity: params.severity || "medium"
|
|
1323
|
+
}
|
|
1324
|
+
});
|
|
1325
|
+
return { success: true };
|
|
1326
|
+
} catch (error) {
|
|
1327
|
+
if (this.client.getCompatMode() !== "fallback" || !isEndpointNotFound(error)) {
|
|
1328
|
+
throw error;
|
|
1329
|
+
}
|
|
1330
|
+
await this.client.request({
|
|
1331
|
+
endpoint: `/v1/memory/${params.memoryId}`,
|
|
1332
|
+
method: "PUT",
|
|
1333
|
+
operation: "writeAck",
|
|
1334
|
+
body: {
|
|
1335
|
+
content: `[FLAGGED:${params.severity || "medium"}] ${params.reason}`
|
|
1336
|
+
}
|
|
1337
|
+
});
|
|
1338
|
+
return { success: true };
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
};
|
|
1342
|
+
|
|
1343
|
+
// ../src/sdk/modules/session.ts
|
|
1344
|
+
function randomSessionId() {
|
|
1345
|
+
return `sess_${stableHash(`${Date.now()}_${Math.random()}`)}`;
|
|
1346
|
+
}
|
|
1347
|
+
function assertTransition(current, next) {
|
|
1348
|
+
const allowed = {
|
|
1349
|
+
created: ["active", "ended", "archived"],
|
|
1350
|
+
active: ["suspended", "ended", "archived"],
|
|
1351
|
+
suspended: ["resumed", "ended", "archived"],
|
|
1352
|
+
resumed: ["suspended", "ended", "archived"],
|
|
1353
|
+
ended: ["archived"],
|
|
1354
|
+
archived: []
|
|
1355
|
+
};
|
|
1356
|
+
if (!allowed[current].includes(next)) {
|
|
1357
|
+
throw new RuntimeClientError({
|
|
1358
|
+
code: "INVALID_SESSION_STATE",
|
|
1359
|
+
message: `Invalid session transition ${current} -> ${next}`,
|
|
1360
|
+
retryable: false
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
var SessionModule = class {
|
|
1365
|
+
constructor(memory, defaultProject) {
|
|
1366
|
+
this.memory = memory;
|
|
1367
|
+
this.defaultProject = defaultProject;
|
|
1368
|
+
}
|
|
1369
|
+
sessions = /* @__PURE__ */ new Map();
|
|
1370
|
+
resolveProject(project) {
|
|
1371
|
+
const value = project || this.defaultProject;
|
|
1372
|
+
if (!value) {
|
|
1373
|
+
throw new RuntimeClientError({
|
|
1374
|
+
code: "MISSING_PROJECT",
|
|
1375
|
+
message: "Project is required",
|
|
1376
|
+
retryable: false
|
|
1377
|
+
});
|
|
1378
|
+
}
|
|
1379
|
+
return value;
|
|
1380
|
+
}
|
|
1381
|
+
ensure(sessionId) {
|
|
1382
|
+
const found = this.sessions.get(sessionId);
|
|
1383
|
+
if (!found) {
|
|
1384
|
+
throw new RuntimeClientError({
|
|
1385
|
+
code: "SESSION_NOT_FOUND",
|
|
1386
|
+
message: `Unknown session ${sessionId}`,
|
|
1387
|
+
retryable: false
|
|
1388
|
+
});
|
|
1389
|
+
}
|
|
1390
|
+
return found;
|
|
1391
|
+
}
|
|
1392
|
+
async start(params) {
|
|
1393
|
+
const project = this.resolveProject(params.project);
|
|
1394
|
+
const sessionId = params.sessionId || randomSessionId();
|
|
1395
|
+
const now = nowIso();
|
|
1396
|
+
const record = {
|
|
1397
|
+
sessionId,
|
|
1398
|
+
project,
|
|
1399
|
+
userId: params.userId,
|
|
1400
|
+
state: "active",
|
|
1401
|
+
sequence: 0,
|
|
1402
|
+
metadata: params.metadata,
|
|
1403
|
+
createdAt: now,
|
|
1404
|
+
updatedAt: now
|
|
1405
|
+
};
|
|
1406
|
+
this.sessions.set(sessionId, record);
|
|
1407
|
+
return {
|
|
1408
|
+
sessionId,
|
|
1409
|
+
state: record.state,
|
|
1410
|
+
createdAt: now
|
|
1411
|
+
};
|
|
1412
|
+
}
|
|
1413
|
+
async event(params) {
|
|
1414
|
+
const session = this.ensure(params.sessionId);
|
|
1415
|
+
if (session.state !== "active" && session.state !== "resumed") {
|
|
1416
|
+
throw new RuntimeClientError({
|
|
1417
|
+
code: "INVALID_SESSION_STATE",
|
|
1418
|
+
message: `Cannot append event in ${session.state} state`,
|
|
1419
|
+
retryable: false
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1422
|
+
session.sequence += 1;
|
|
1423
|
+
session.updatedAt = nowIso();
|
|
1424
|
+
const eventId = `evt_${stableHash(JSON.stringify({
|
|
1425
|
+
sessionId: session.sessionId,
|
|
1426
|
+
seq: session.sequence,
|
|
1427
|
+
type: params.type,
|
|
1428
|
+
content: params.content,
|
|
1429
|
+
parent: params.parentEventId || ""
|
|
1430
|
+
}))}`;
|
|
1431
|
+
await this.memory.add({
|
|
1432
|
+
project: session.project,
|
|
1433
|
+
content: `${params.type}: ${params.content}`,
|
|
1434
|
+
memory_type: "event",
|
|
1435
|
+
user_id: session.userId,
|
|
1436
|
+
session_id: session.sessionId,
|
|
1437
|
+
metadata: {
|
|
1438
|
+
session_event: true,
|
|
1439
|
+
event_id: eventId,
|
|
1440
|
+
sequence: session.sequence,
|
|
1441
|
+
parent_event_id: params.parentEventId,
|
|
1442
|
+
...session.metadata,
|
|
1443
|
+
...params.metadata || {}
|
|
1444
|
+
},
|
|
1445
|
+
write_mode: "async"
|
|
1446
|
+
});
|
|
1447
|
+
return {
|
|
1448
|
+
success: true,
|
|
1449
|
+
eventId,
|
|
1450
|
+
sequence: session.sequence
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1453
|
+
async suspend(params) {
|
|
1454
|
+
const session = this.ensure(params.sessionId);
|
|
1455
|
+
assertTransition(session.state, "suspended");
|
|
1456
|
+
session.state = "suspended";
|
|
1457
|
+
session.updatedAt = nowIso();
|
|
1458
|
+
return { sessionId: session.sessionId, state: session.state };
|
|
1459
|
+
}
|
|
1460
|
+
async resume(params) {
|
|
1461
|
+
const session = this.ensure(params.sessionId);
|
|
1462
|
+
const target = session.state === "suspended" ? "resumed" : "active";
|
|
1463
|
+
assertTransition(session.state, target);
|
|
1464
|
+
session.state = target;
|
|
1465
|
+
session.updatedAt = nowIso();
|
|
1466
|
+
return { sessionId: session.sessionId, state: session.state };
|
|
1467
|
+
}
|
|
1468
|
+
async end(params) {
|
|
1469
|
+
const session = this.ensure(params.sessionId);
|
|
1470
|
+
assertTransition(session.state, "ended");
|
|
1471
|
+
session.state = "ended";
|
|
1472
|
+
session.updatedAt = nowIso();
|
|
1473
|
+
return { sessionId: session.sessionId, state: session.state };
|
|
1474
|
+
}
|
|
1475
|
+
async archive(params) {
|
|
1476
|
+
const session = this.ensure(params.sessionId);
|
|
1477
|
+
assertTransition(session.state, "archived");
|
|
1478
|
+
session.state = "archived";
|
|
1479
|
+
session.updatedAt = nowIso();
|
|
1480
|
+
return { sessionId: session.sessionId, state: session.state };
|
|
1481
|
+
}
|
|
1482
|
+
};
|
|
1483
|
+
|
|
1484
|
+
// ../src/sdk/modules/profile.ts
|
|
1485
|
+
var ProfileModule = class {
|
|
1486
|
+
constructor(memory) {
|
|
1487
|
+
this.memory = memory;
|
|
1488
|
+
}
|
|
1489
|
+
async getUserProfile(params) {
|
|
1490
|
+
return this.memory.getUserProfile(params);
|
|
1491
|
+
}
|
|
1492
|
+
async getSessionMemories(params) {
|
|
1493
|
+
return this.memory.getSessionMemories(params);
|
|
1494
|
+
}
|
|
1495
|
+
};
|
|
1496
|
+
|
|
1497
|
+
// ../src/sdk/modules/analytics.ts
|
|
1498
|
+
var AnalyticsModule = class {
|
|
1499
|
+
constructor(diagnostics, queue) {
|
|
1500
|
+
this.diagnostics = diagnostics;
|
|
1501
|
+
this.queue = queue;
|
|
1502
|
+
}
|
|
1503
|
+
diagnosticsSnapshot() {
|
|
1504
|
+
return this.diagnostics.snapshot();
|
|
1505
|
+
}
|
|
1506
|
+
queueStatus() {
|
|
1507
|
+
return this.queue.status();
|
|
1508
|
+
}
|
|
1509
|
+
};
|
|
1510
|
+
|
|
1511
|
+
// ../src/sdk/whisper.ts
|
|
1512
|
+
var WhisperClient = class _WhisperClient {
|
|
1513
|
+
constructor(config) {
|
|
1514
|
+
this.config = config;
|
|
1515
|
+
this.diagnosticsStore = new DiagnosticsStore(config.telemetry?.maxEntries || 1e3);
|
|
1516
|
+
this.runtimeClient = new RuntimeClient(
|
|
1517
|
+
{
|
|
1518
|
+
apiKey: config.apiKey,
|
|
1519
|
+
baseUrl: config.baseUrl,
|
|
1520
|
+
compatMode: config.compatMode || "fallback",
|
|
1521
|
+
timeouts: config.timeouts,
|
|
1522
|
+
retryPolicy: config.retryPolicy
|
|
1523
|
+
},
|
|
1524
|
+
this.diagnosticsStore
|
|
1525
|
+
);
|
|
1526
|
+
this.searchCache = new SearchResponseCache(
|
|
1527
|
+
config.cache?.ttlMs ?? 7e3,
|
|
1528
|
+
config.cache?.capacity ?? 500
|
|
1529
|
+
);
|
|
1530
|
+
const queueStore = config.queue?.persistence === "storage" ? createStorageQueueStore() : config.queue?.persistence === "file" && config.queue.filePath ? createFileQueueStore(config.queue.filePath) : new InMemoryQueueStore();
|
|
1531
|
+
this.writeQueue = new WriteQueue({
|
|
1532
|
+
store: queueStore,
|
|
1533
|
+
maxBatchSize: config.queue?.maxBatchSize ?? 50,
|
|
1534
|
+
flushIntervalMs: config.queue?.flushIntervalMs ?? 100,
|
|
1535
|
+
maxAttempts: config.queue?.maxAttempts ?? 2,
|
|
1536
|
+
flushHandler: async (items) => {
|
|
1537
|
+
if (items.length === 0) return;
|
|
1538
|
+
const project = items[0].project;
|
|
1539
|
+
const memories = items.map((item) => ({
|
|
1540
|
+
...item.payload,
|
|
1541
|
+
user_id: item.payload.user_id ?? item.userId,
|
|
1542
|
+
session_id: item.payload.session_id ?? item.sessionId,
|
|
1543
|
+
metadata: {
|
|
1544
|
+
...item.payload.metadata || {},
|
|
1545
|
+
event_id: item.eventId,
|
|
1546
|
+
queued_at: item.createdAt
|
|
1547
|
+
}
|
|
1548
|
+
}));
|
|
1549
|
+
try {
|
|
1550
|
+
await this.runtimeClient.request({
|
|
1551
|
+
endpoint: "/v1/memory/bulk",
|
|
1552
|
+
method: "POST",
|
|
1553
|
+
operation: "bulk",
|
|
1554
|
+
body: {
|
|
1555
|
+
project,
|
|
1556
|
+
write_mode: "async",
|
|
1557
|
+
memories
|
|
1558
|
+
}
|
|
1559
|
+
});
|
|
1560
|
+
} catch (error) {
|
|
1561
|
+
if (this.runtimeClient.getCompatMode() !== "fallback" || !(error instanceof RuntimeClientError) || error.status !== 404) {
|
|
1562
|
+
throw error;
|
|
1563
|
+
}
|
|
1564
|
+
await Promise.all(
|
|
1565
|
+
memories.map(async (memory) => {
|
|
1566
|
+
try {
|
|
1567
|
+
await this.runtimeClient.request({
|
|
1568
|
+
endpoint: "/v1/memory",
|
|
1569
|
+
method: "POST",
|
|
1570
|
+
operation: "writeAck",
|
|
1571
|
+
body: {
|
|
1572
|
+
project,
|
|
1573
|
+
...memory,
|
|
1574
|
+
write_mode: "sync"
|
|
1575
|
+
}
|
|
1576
|
+
});
|
|
1577
|
+
} catch (fallbackError) {
|
|
1578
|
+
if (this.runtimeClient.getCompatMode() !== "fallback" || !(fallbackError instanceof RuntimeClientError) || fallbackError.status !== 404) {
|
|
1579
|
+
throw fallbackError;
|
|
1580
|
+
}
|
|
1581
|
+
await this.runtimeClient.request({
|
|
1582
|
+
endpoint: "/v1/memories",
|
|
1583
|
+
method: "POST",
|
|
1584
|
+
operation: "writeAck",
|
|
1585
|
+
body: {
|
|
1586
|
+
project,
|
|
1587
|
+
...memory,
|
|
1588
|
+
memory_type: memory.memory_type === "event" ? "episodic" : memory.memory_type
|
|
1589
|
+
}
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
})
|
|
1593
|
+
);
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
});
|
|
1597
|
+
if (config.queue?.enabled !== false) {
|
|
1598
|
+
void this.writeQueue.start();
|
|
1599
|
+
}
|
|
1600
|
+
this.memoryModule = new MemoryModule(
|
|
1601
|
+
this.runtimeClient,
|
|
1602
|
+
this.searchCache,
|
|
1603
|
+
this.writeQueue,
|
|
1604
|
+
{
|
|
1605
|
+
defaultProject: config.project,
|
|
1606
|
+
cacheEnabled: config.cache?.enabled !== false,
|
|
1607
|
+
queueEnabled: config.queue?.enabled !== false
|
|
1608
|
+
}
|
|
1609
|
+
);
|
|
1610
|
+
this.sessionModule = new SessionModule(this.memoryModule, config.project);
|
|
1611
|
+
this.profileModule = new ProfileModule(this.memoryModule);
|
|
1612
|
+
this.analyticsModule = new AnalyticsModule(this.diagnosticsStore, this.writeQueue);
|
|
1613
|
+
this.diagnostics = {
|
|
1614
|
+
getLast: (limit) => this.diagnosticsStore.getLast(limit),
|
|
1615
|
+
subscribe: (fn) => this.diagnosticsStore.subscribe(fn),
|
|
1616
|
+
snapshot: () => this.diagnosticsStore.snapshot()
|
|
1617
|
+
};
|
|
1618
|
+
this.queue = {
|
|
1619
|
+
flush: () => this.writeQueue.flush(),
|
|
1620
|
+
status: () => this.writeQueue.status()
|
|
1621
|
+
};
|
|
1622
|
+
this.memory = {
|
|
1623
|
+
add: (params) => this.memoryModule.add(params),
|
|
1624
|
+
addBulk: (params) => this.memoryModule.addBulk(params),
|
|
1625
|
+
search: (params) => this.memoryModule.search(params),
|
|
1626
|
+
getUserProfile: (params) => this.memoryModule.getUserProfile(params),
|
|
1627
|
+
getSessionMemories: (params) => this.memoryModule.getSessionMemories(params),
|
|
1628
|
+
update: (memoryId, params) => this.memoryModule.update(memoryId, params),
|
|
1629
|
+
delete: (memoryId) => this.memoryModule.delete(memoryId),
|
|
1630
|
+
flag: (params) => this.memoryModule.flag(params)
|
|
1631
|
+
};
|
|
1632
|
+
this.session = {
|
|
1633
|
+
start: (params) => this.sessionModule.start(params),
|
|
1634
|
+
event: (params) => this.sessionModule.event(params),
|
|
1635
|
+
suspend: (params) => this.sessionModule.suspend(params),
|
|
1636
|
+
resume: (params) => this.sessionModule.resume(params),
|
|
1637
|
+
end: (params) => this.sessionModule.end(params)
|
|
1638
|
+
};
|
|
1639
|
+
this.profile = {
|
|
1640
|
+
getUserProfile: (params) => this.profileModule.getUserProfile(params),
|
|
1641
|
+
getSessionMemories: (params) => this.profileModule.getSessionMemories(params)
|
|
1642
|
+
};
|
|
1643
|
+
this.analytics = {
|
|
1644
|
+
diagnosticsSnapshot: () => this.analyticsModule.diagnosticsSnapshot(),
|
|
1645
|
+
queueStatus: () => this.analyticsModule.queueStatus()
|
|
1646
|
+
};
|
|
1647
|
+
}
|
|
1648
|
+
diagnostics;
|
|
1649
|
+
queue;
|
|
1650
|
+
memory;
|
|
1651
|
+
session;
|
|
1652
|
+
profile;
|
|
1653
|
+
analytics;
|
|
1654
|
+
runtimeClient;
|
|
1655
|
+
diagnosticsStore;
|
|
1656
|
+
searchCache;
|
|
1657
|
+
writeQueue;
|
|
1658
|
+
memoryModule;
|
|
1659
|
+
sessionModule;
|
|
1660
|
+
profileModule;
|
|
1661
|
+
analyticsModule;
|
|
1662
|
+
static fromEnv(overrides = {}) {
|
|
1663
|
+
const env = typeof process !== "undefined" ? process.env : {};
|
|
1664
|
+
const apiKey = overrides.apiKey || env.WHISPER_API_KEY || env.USEWHISPER_API_KEY || env.API_KEY;
|
|
1665
|
+
if (!apiKey) {
|
|
1666
|
+
throw new Error("Missing API key. Set WHISPER_API_KEY / USEWHISPER_API_KEY / API_KEY.");
|
|
242
1667
|
}
|
|
243
|
-
return
|
|
1668
|
+
return new _WhisperClient({
|
|
1669
|
+
apiKey,
|
|
1670
|
+
baseUrl: overrides.baseUrl || env.WHISPER_BASE_URL || env.API_BASE_URL || "https://context.usewhisper.dev",
|
|
1671
|
+
project: overrides.project || env.WHISPER_PROJECT || env.PROJECT,
|
|
1672
|
+
...overrides
|
|
1673
|
+
});
|
|
1674
|
+
}
|
|
1675
|
+
withRunContext(context) {
|
|
1676
|
+
const base = this;
|
|
1677
|
+
return {
|
|
1678
|
+
memory: {
|
|
1679
|
+
add: (params) => base.memory.add({
|
|
1680
|
+
...params,
|
|
1681
|
+
project: params.project || context.project || base.config.project,
|
|
1682
|
+
user_id: params.user_id || context.userId,
|
|
1683
|
+
session_id: params.session_id || context.sessionId
|
|
1684
|
+
}),
|
|
1685
|
+
search: (params) => base.memory.search({
|
|
1686
|
+
...params,
|
|
1687
|
+
project: params.project || context.project || base.config.project,
|
|
1688
|
+
user_id: params.user_id || context.userId,
|
|
1689
|
+
session_id: params.session_id || context.sessionId
|
|
1690
|
+
})
|
|
1691
|
+
},
|
|
1692
|
+
session: {
|
|
1693
|
+
event: (params) => base.session.event({
|
|
1694
|
+
...params,
|
|
1695
|
+
sessionId: params.sessionId || context.sessionId || ""
|
|
1696
|
+
})
|
|
1697
|
+
},
|
|
1698
|
+
queue: base.queue,
|
|
1699
|
+
diagnostics: base.diagnostics
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1702
|
+
async shutdown() {
|
|
1703
|
+
await this.writeQueue.stop();
|
|
244
1704
|
}
|
|
245
1705
|
};
|
|
246
|
-
var
|
|
1706
|
+
var whisper_default = WhisperClient;
|
|
247
1707
|
|
|
248
1708
|
// ../src/sdk/middleware.ts
|
|
249
1709
|
var WhisperAgentMiddleware = class {
|
|
@@ -314,14 +1774,409 @@ function createAgentMiddleware(config) {
|
|
|
314
1774
|
return new WhisperAgentMiddleware(config);
|
|
315
1775
|
}
|
|
316
1776
|
|
|
1777
|
+
// ../src/sdk/adapters/langchain.ts
|
|
1778
|
+
var LangChainMemoryAdapter = class {
|
|
1779
|
+
constructor(client, options) {
|
|
1780
|
+
this.client = client;
|
|
1781
|
+
this.options = options;
|
|
1782
|
+
this.memoryKey = options.memoryKey || "history";
|
|
1783
|
+
this.memoryKeys = [this.memoryKey];
|
|
1784
|
+
}
|
|
1785
|
+
memoryKeys;
|
|
1786
|
+
memoryKey;
|
|
1787
|
+
async loadMemoryVariables(_inputValues) {
|
|
1788
|
+
const response = await this.client.memory.search({
|
|
1789
|
+
project: this.options.project,
|
|
1790
|
+
query: "recent context",
|
|
1791
|
+
user_id: this.options.userId,
|
|
1792
|
+
session_id: this.options.sessionId,
|
|
1793
|
+
top_k: this.options.topK || 10,
|
|
1794
|
+
profile: this.options.profile || "fast",
|
|
1795
|
+
include_pending: true
|
|
1796
|
+
});
|
|
1797
|
+
const content = (response.results || []).map((row) => row.memory?.content || "").filter(Boolean).join("\n");
|
|
1798
|
+
return {
|
|
1799
|
+
[this.memoryKey]: content
|
|
1800
|
+
};
|
|
1801
|
+
}
|
|
1802
|
+
async saveContext(inputValues, outputValues) {
|
|
1803
|
+
const userInput = typeof inputValues.input === "string" ? inputValues.input : JSON.stringify(inputValues);
|
|
1804
|
+
const output = typeof outputValues.output === "string" ? outputValues.output : JSON.stringify(outputValues);
|
|
1805
|
+
await this.client.memory.addBulk({
|
|
1806
|
+
project: this.options.project,
|
|
1807
|
+
write_mode: "async",
|
|
1808
|
+
memories: [
|
|
1809
|
+
{
|
|
1810
|
+
content: `user: ${userInput}`,
|
|
1811
|
+
memory_type: "event",
|
|
1812
|
+
user_id: this.options.userId,
|
|
1813
|
+
session_id: this.options.sessionId
|
|
1814
|
+
},
|
|
1815
|
+
{
|
|
1816
|
+
content: `assistant: ${output}`,
|
|
1817
|
+
memory_type: "event",
|
|
1818
|
+
user_id: this.options.userId,
|
|
1819
|
+
session_id: this.options.sessionId
|
|
1820
|
+
}
|
|
1821
|
+
]
|
|
1822
|
+
});
|
|
1823
|
+
}
|
|
1824
|
+
async clear() {
|
|
1825
|
+
const profile = await this.client.memory.getUserProfile({
|
|
1826
|
+
project: this.options.project,
|
|
1827
|
+
user_id: this.options.userId
|
|
1828
|
+
});
|
|
1829
|
+
await Promise.all(
|
|
1830
|
+
(profile.memories || []).map((memory) => String(memory.id || "")).filter(Boolean).map((id) => this.client.memory.delete(id))
|
|
1831
|
+
);
|
|
1832
|
+
}
|
|
1833
|
+
};
|
|
1834
|
+
function createLangChainMemoryAdapter(client, options) {
|
|
1835
|
+
return new LangChainMemoryAdapter(client, options);
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
// ../src/sdk/adapters/langgraph.ts
|
|
1839
|
+
function asRecord(value) {
|
|
1840
|
+
return value && typeof value === "object" ? value : {};
|
|
1841
|
+
}
|
|
1842
|
+
function text(value) {
|
|
1843
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
1844
|
+
}
|
|
1845
|
+
function isTupleLike(value) {
|
|
1846
|
+
if (!value || typeof value !== "object") return false;
|
|
1847
|
+
const tuple = value;
|
|
1848
|
+
const config = asRecord(tuple.config);
|
|
1849
|
+
const configurable = asRecord(config.configurable);
|
|
1850
|
+
return Boolean(
|
|
1851
|
+
typeof configurable.thread_id === "string" && tuple.checkpoint && typeof tuple.checkpoint === "object"
|
|
1852
|
+
);
|
|
1853
|
+
}
|
|
1854
|
+
function tupleTimestamp(tuple, fallback) {
|
|
1855
|
+
const checkpoint = asRecord(tuple.checkpoint);
|
|
1856
|
+
const metadata = asRecord(tuple.metadata);
|
|
1857
|
+
const sources = [
|
|
1858
|
+
checkpoint.updatedAt,
|
|
1859
|
+
checkpoint.updated_at,
|
|
1860
|
+
checkpoint.ts,
|
|
1861
|
+
checkpoint.timestamp,
|
|
1862
|
+
metadata.updatedAt,
|
|
1863
|
+
metadata.updated_at,
|
|
1864
|
+
metadata.ts,
|
|
1865
|
+
metadata.timestamp,
|
|
1866
|
+
fallback
|
|
1867
|
+
];
|
|
1868
|
+
for (const source of sources) {
|
|
1869
|
+
const parsed = source ? new Date(String(source)).getTime() : Number.NaN;
|
|
1870
|
+
if (!Number.isNaN(parsed) && parsed > 0) return parsed;
|
|
1871
|
+
}
|
|
1872
|
+
return 0;
|
|
1873
|
+
}
|
|
1874
|
+
function metadataMatches(target, filter) {
|
|
1875
|
+
if (!filter || Object.keys(filter).length === 0) return true;
|
|
1876
|
+
const value = target || {};
|
|
1877
|
+
return Object.entries(filter).every(([key, expected]) => {
|
|
1878
|
+
if (!(key in value)) return false;
|
|
1879
|
+
return JSON.stringify(value[key]) === JSON.stringify(expected);
|
|
1880
|
+
});
|
|
1881
|
+
}
|
|
1882
|
+
var LangGraphCheckpointAdapter = class {
|
|
1883
|
+
constructor(client, options = {}) {
|
|
1884
|
+
this.client = client;
|
|
1885
|
+
this.options = options;
|
|
1886
|
+
}
|
|
1887
|
+
localByKey = /* @__PURE__ */ new Map();
|
|
1888
|
+
localByThread = /* @__PURE__ */ new Map();
|
|
1889
|
+
options;
|
|
1890
|
+
getUserId(threadId) {
|
|
1891
|
+
const prefix = this.options.userIdPrefix || "langgraph-thread";
|
|
1892
|
+
return `${prefix}:${threadId}`;
|
|
1893
|
+
}
|
|
1894
|
+
resolveCheckpointNs(config) {
|
|
1895
|
+
return config.configurable.checkpoint_ns || this.options.defaultCheckpointNs || "default";
|
|
1896
|
+
}
|
|
1897
|
+
makeLocalKey(threadId, checkpointNs, checkpointId) {
|
|
1898
|
+
return `${threadId}:${checkpointNs}:${checkpointId}`;
|
|
1899
|
+
}
|
|
1900
|
+
normalizeTuple(tuple) {
|
|
1901
|
+
const config = asRecord(tuple.config);
|
|
1902
|
+
const configurable = asRecord(config.configurable);
|
|
1903
|
+
const threadId = text(configurable.thread_id) || "";
|
|
1904
|
+
const checkpointNs = text(configurable.checkpoint_ns) || this.options.defaultCheckpointNs || "default";
|
|
1905
|
+
const checkpointId = text(configurable.checkpoint_id) || "";
|
|
1906
|
+
return {
|
|
1907
|
+
config: {
|
|
1908
|
+
configurable: {
|
|
1909
|
+
thread_id: threadId,
|
|
1910
|
+
checkpoint_ns: checkpointNs,
|
|
1911
|
+
checkpoint_id: checkpointId || void 0
|
|
1912
|
+
}
|
|
1913
|
+
},
|
|
1914
|
+
checkpoint: asRecord(tuple.checkpoint),
|
|
1915
|
+
metadata: asRecord(tuple.metadata),
|
|
1916
|
+
parent_config: tuple.parent_config ? {
|
|
1917
|
+
configurable: {
|
|
1918
|
+
thread_id: text(tuple.parent_config.configurable.thread_id) || "",
|
|
1919
|
+
checkpoint_ns: text(tuple.parent_config.configurable.checkpoint_ns) || checkpointNs,
|
|
1920
|
+
checkpoint_id: text(tuple.parent_config.configurable.checkpoint_id)
|
|
1921
|
+
}
|
|
1922
|
+
} : null
|
|
1923
|
+
};
|
|
1924
|
+
}
|
|
1925
|
+
parseCheckpointTupleFromRow(row) {
|
|
1926
|
+
const rowMetadata = asRecord(row.metadata);
|
|
1927
|
+
const marker = rowMetadata.langgraph_checkpoint === true;
|
|
1928
|
+
const rawContent = text(row.content) || "";
|
|
1929
|
+
if (!rawContent) return null;
|
|
1930
|
+
try {
|
|
1931
|
+
const parsed = JSON.parse(rawContent);
|
|
1932
|
+
let tuple = null;
|
|
1933
|
+
if (isTupleLike(parsed)) {
|
|
1934
|
+
tuple = this.normalizeTuple(parsed);
|
|
1935
|
+
} else {
|
|
1936
|
+
const wrapped = asRecord(parsed);
|
|
1937
|
+
if (isTupleLike(wrapped.tuple)) {
|
|
1938
|
+
tuple = this.normalizeTuple(wrapped.tuple);
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
if (!tuple) return null;
|
|
1942
|
+
const config = tuple.config.configurable;
|
|
1943
|
+
if (!config.thread_id) return null;
|
|
1944
|
+
if (!config.checkpoint_id) return null;
|
|
1945
|
+
if (!marker && rowMetadata.checkpoint_id !== config.checkpoint_id) {
|
|
1946
|
+
return null;
|
|
1947
|
+
}
|
|
1948
|
+
return {
|
|
1949
|
+
tuple,
|
|
1950
|
+
memoryId: text(row.id),
|
|
1951
|
+
createdAt: text(row.createdAt) || text(row.created_at),
|
|
1952
|
+
updatedAt: text(row.updatedAt) || text(row.updated_at)
|
|
1953
|
+
};
|
|
1954
|
+
} catch {
|
|
1955
|
+
return null;
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
upsertLocal(record) {
|
|
1959
|
+
const cfg = record.tuple.config.configurable;
|
|
1960
|
+
const key = this.makeLocalKey(cfg.thread_id, cfg.checkpoint_ns || "default", cfg.checkpoint_id || "");
|
|
1961
|
+
this.localByKey.set(key, record);
|
|
1962
|
+
if (!this.localByThread.has(cfg.thread_id)) {
|
|
1963
|
+
this.localByThread.set(cfg.thread_id, /* @__PURE__ */ new Set());
|
|
1964
|
+
}
|
|
1965
|
+
this.localByThread.get(cfg.thread_id).add(key);
|
|
1966
|
+
}
|
|
1967
|
+
mergeWithLocal(records, threadId) {
|
|
1968
|
+
const merged = /* @__PURE__ */ new Map();
|
|
1969
|
+
for (const record of records) {
|
|
1970
|
+
const cfg = record.tuple.config.configurable;
|
|
1971
|
+
const key = this.makeLocalKey(cfg.thread_id, cfg.checkpoint_ns || "default", cfg.checkpoint_id || "");
|
|
1972
|
+
merged.set(key, record);
|
|
1973
|
+
}
|
|
1974
|
+
const localKeys = this.localByThread.get(threadId);
|
|
1975
|
+
if (localKeys) {
|
|
1976
|
+
for (const key of localKeys) {
|
|
1977
|
+
const local = this.localByKey.get(key);
|
|
1978
|
+
if (local) {
|
|
1979
|
+
merged.set(key, local);
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
return Array.from(merged.values());
|
|
1984
|
+
}
|
|
1985
|
+
applyListFilters(records, options) {
|
|
1986
|
+
let filtered = records;
|
|
1987
|
+
if (options?.filter?.checkpointNs) {
|
|
1988
|
+
filtered = filtered.filter(
|
|
1989
|
+
(record) => (record.tuple.config.configurable.checkpoint_ns || "default") === options.filter.checkpointNs
|
|
1990
|
+
);
|
|
1991
|
+
}
|
|
1992
|
+
if (options?.filter?.metadata) {
|
|
1993
|
+
filtered = filtered.filter((record) => metadataMatches(record.tuple.metadata, options.filter.metadata));
|
|
1994
|
+
}
|
|
1995
|
+
if (options?.before?.checkpointId) {
|
|
1996
|
+
const beforeId = options.before.checkpointId;
|
|
1997
|
+
filtered = filtered.filter(
|
|
1998
|
+
(record) => record.tuple.config.configurable.checkpoint_id !== beforeId
|
|
1999
|
+
);
|
|
2000
|
+
}
|
|
2001
|
+
if (options?.before?.updatedAt) {
|
|
2002
|
+
const cutoff = new Date(options.before.updatedAt).getTime();
|
|
2003
|
+
if (!Number.isNaN(cutoff)) {
|
|
2004
|
+
filtered = filtered.filter((record) => {
|
|
2005
|
+
const value = tupleTimestamp(record.tuple, record.updatedAt || record.createdAt);
|
|
2006
|
+
return value < cutoff;
|
|
2007
|
+
});
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
const direction = options?.sort || "desc";
|
|
2011
|
+
filtered.sort((a, b) => {
|
|
2012
|
+
const ta = tupleTimestamp(a.tuple, a.updatedAt || a.createdAt);
|
|
2013
|
+
const tb = tupleTimestamp(b.tuple, b.updatedAt || b.createdAt);
|
|
2014
|
+
return direction === "asc" ? ta - tb : tb - ta;
|
|
2015
|
+
});
|
|
2016
|
+
if (options?.limit && options.limit > 0) {
|
|
2017
|
+
return filtered.slice(0, options.limit);
|
|
2018
|
+
}
|
|
2019
|
+
return filtered;
|
|
2020
|
+
}
|
|
2021
|
+
async fetchThreadRecords(threadId) {
|
|
2022
|
+
const profile = await this.client.memory.getUserProfile({
|
|
2023
|
+
project: this.options.project,
|
|
2024
|
+
user_id: this.getUserId(threadId),
|
|
2025
|
+
include_pending: true
|
|
2026
|
+
});
|
|
2027
|
+
const parsed = (profile.memories || []).map((row) => this.parseCheckpointTupleFromRow(row)).filter((value) => value !== null).filter((record) => record.tuple.config.configurable.thread_id === threadId);
|
|
2028
|
+
const merged = this.mergeWithLocal(parsed, threadId);
|
|
2029
|
+
for (const record of merged) {
|
|
2030
|
+
this.upsertLocal(record);
|
|
2031
|
+
}
|
|
2032
|
+
return merged;
|
|
2033
|
+
}
|
|
2034
|
+
async get(config) {
|
|
2035
|
+
const threadId = config.configurable.thread_id;
|
|
2036
|
+
const checkpointNs = this.resolveCheckpointNs(config);
|
|
2037
|
+
const checkpointId = config.configurable.checkpoint_id;
|
|
2038
|
+
if (checkpointId) {
|
|
2039
|
+
const local = this.localByKey.get(this.makeLocalKey(threadId, checkpointNs, checkpointId));
|
|
2040
|
+
if (local) return local.tuple;
|
|
2041
|
+
}
|
|
2042
|
+
const records = await this.fetchThreadRecords(threadId);
|
|
2043
|
+
const scoped = records.filter((record) => {
|
|
2044
|
+
const cfg = record.tuple.config.configurable;
|
|
2045
|
+
if ((cfg.checkpoint_ns || "default") !== checkpointNs) return false;
|
|
2046
|
+
if (!checkpointId) return true;
|
|
2047
|
+
return cfg.checkpoint_id === checkpointId;
|
|
2048
|
+
});
|
|
2049
|
+
if (scoped.length === 0) return void 0;
|
|
2050
|
+
scoped.sort((a, b) => {
|
|
2051
|
+
const ta = tupleTimestamp(a.tuple, a.updatedAt || a.createdAt);
|
|
2052
|
+
const tb = tupleTimestamp(b.tuple, b.updatedAt || b.createdAt);
|
|
2053
|
+
return tb - ta;
|
|
2054
|
+
});
|
|
2055
|
+
return scoped[0].tuple;
|
|
2056
|
+
}
|
|
2057
|
+
async put(config, checkpoint, metadata, parentConfig) {
|
|
2058
|
+
const threadId = config.configurable.thread_id;
|
|
2059
|
+
const checkpointNs = this.resolveCheckpointNs(config);
|
|
2060
|
+
const checkpointId = config.configurable.checkpoint_id || text(checkpoint.id) || `cp_${stableHash(JSON.stringify({
|
|
2061
|
+
threadId,
|
|
2062
|
+
checkpointNs,
|
|
2063
|
+
checkpoint,
|
|
2064
|
+
metadata: metadata || {},
|
|
2065
|
+
parentConfig: parentConfig || null
|
|
2066
|
+
}))}`;
|
|
2067
|
+
const tuple = this.normalizeTuple({
|
|
2068
|
+
config: {
|
|
2069
|
+
configurable: {
|
|
2070
|
+
thread_id: threadId,
|
|
2071
|
+
checkpoint_ns: checkpointNs,
|
|
2072
|
+
checkpoint_id: checkpointId
|
|
2073
|
+
}
|
|
2074
|
+
},
|
|
2075
|
+
checkpoint: {
|
|
2076
|
+
...checkpoint,
|
|
2077
|
+
id: checkpointId
|
|
2078
|
+
},
|
|
2079
|
+
metadata: {
|
|
2080
|
+
...metadata || {},
|
|
2081
|
+
checkpoint_ns: checkpointNs,
|
|
2082
|
+
written_at: nowIso()
|
|
2083
|
+
},
|
|
2084
|
+
parent_config: parentConfig ? this.normalizeTuple({
|
|
2085
|
+
config: parentConfig,
|
|
2086
|
+
checkpoint: {},
|
|
2087
|
+
metadata: {},
|
|
2088
|
+
parent_config: null
|
|
2089
|
+
}).config : null
|
|
2090
|
+
});
|
|
2091
|
+
const record = { tuple, updatedAt: nowIso() };
|
|
2092
|
+
this.upsertLocal(record);
|
|
2093
|
+
await this.client.memory.add({
|
|
2094
|
+
project: this.options.project,
|
|
2095
|
+
user_id: this.getUserId(threadId),
|
|
2096
|
+
session_id: threadId,
|
|
2097
|
+
memory_type: "event",
|
|
2098
|
+
write_mode: "async",
|
|
2099
|
+
content: JSON.stringify(tuple),
|
|
2100
|
+
metadata: {
|
|
2101
|
+
langgraph_checkpoint: true,
|
|
2102
|
+
thread_id: threadId,
|
|
2103
|
+
checkpoint_ns: checkpointNs,
|
|
2104
|
+
checkpoint_id: checkpointId,
|
|
2105
|
+
parent_checkpoint_id: parentConfig?.configurable.checkpoint_id
|
|
2106
|
+
}
|
|
2107
|
+
});
|
|
2108
|
+
return tuple.config;
|
|
2109
|
+
}
|
|
2110
|
+
async list(config, options) {
|
|
2111
|
+
const threadId = config.configurable.thread_id;
|
|
2112
|
+
const checkpointNs = this.resolveCheckpointNs(config);
|
|
2113
|
+
const records = await this.fetchThreadRecords(threadId);
|
|
2114
|
+
const scoped = records.filter(
|
|
2115
|
+
(record) => (record.tuple.config.configurable.checkpoint_ns || "default") === checkpointNs
|
|
2116
|
+
);
|
|
2117
|
+
return this.applyListFilters(scoped, options).map((record) => record.tuple);
|
|
2118
|
+
}
|
|
2119
|
+
async search(params) {
|
|
2120
|
+
const includePending = params.includePending !== false;
|
|
2121
|
+
const profile = params.profile || "fast";
|
|
2122
|
+
const checkpointNs = params.checkpointNs || this.options.defaultCheckpointNs || "default";
|
|
2123
|
+
const response = await this.client.memory.search({
|
|
2124
|
+
project: this.options.project,
|
|
2125
|
+
query: params.query,
|
|
2126
|
+
user_id: this.getUserId(params.threadId),
|
|
2127
|
+
session_id: params.threadId,
|
|
2128
|
+
top_k: params.topK || 10,
|
|
2129
|
+
include_pending: includePending,
|
|
2130
|
+
profile
|
|
2131
|
+
});
|
|
2132
|
+
const serverHits = (response.results || []).map((row) => {
|
|
2133
|
+
const content = text(row.memory?.content);
|
|
2134
|
+
if (!content) return null;
|
|
2135
|
+
try {
|
|
2136
|
+
const parsed = JSON.parse(content);
|
|
2137
|
+
if (!isTupleLike(parsed)) return null;
|
|
2138
|
+
const tuple = this.normalizeTuple(parsed);
|
|
2139
|
+
const ns = tuple.config.configurable.checkpoint_ns || "default";
|
|
2140
|
+
if (tuple.config.configurable.thread_id !== params.threadId) return null;
|
|
2141
|
+
if (ns !== checkpointNs) return null;
|
|
2142
|
+
return { tuple, score: row.similarity || 0 };
|
|
2143
|
+
} catch {
|
|
2144
|
+
return null;
|
|
2145
|
+
}
|
|
2146
|
+
}).filter((value) => value !== null);
|
|
2147
|
+
const merged = /* @__PURE__ */ new Map();
|
|
2148
|
+
for (const hit of serverHits) {
|
|
2149
|
+
const cfg = hit.tuple.config.configurable;
|
|
2150
|
+
const key = this.makeLocalKey(cfg.thread_id, cfg.checkpoint_ns || "default", cfg.checkpoint_id || "");
|
|
2151
|
+
merged.set(key, hit);
|
|
2152
|
+
}
|
|
2153
|
+
const localKeys = this.localByThread.get(params.threadId);
|
|
2154
|
+
if (localKeys && includePending) {
|
|
2155
|
+
for (const key of localKeys) {
|
|
2156
|
+
const local = this.localByKey.get(key);
|
|
2157
|
+
if (!local) continue;
|
|
2158
|
+
const cfg = local.tuple.config.configurable;
|
|
2159
|
+
if ((cfg.checkpoint_ns || "default") !== checkpointNs) continue;
|
|
2160
|
+
if (!merged.has(key)) {
|
|
2161
|
+
merged.set(key, { tuple: local.tuple, score: 0 });
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
return Array.from(merged.values()).sort((a, b) => b.score - a.score).map((row) => row.tuple).slice(0, params.topK || 10);
|
|
2166
|
+
}
|
|
2167
|
+
};
|
|
2168
|
+
function createLangGraphCheckpointAdapter(client, options = {}) {
|
|
2169
|
+
return new LangGraphCheckpointAdapter(client, options);
|
|
2170
|
+
}
|
|
2171
|
+
|
|
317
2172
|
// ../src/sdk/graph-utils.ts
|
|
318
2173
|
function sanitizeId(id) {
|
|
319
2174
|
return `n_${id.replace(/[^a-zA-Z0-9_]/g, "_")}`;
|
|
320
2175
|
}
|
|
321
2176
|
function shortLabel(input, max = 48) {
|
|
322
|
-
const
|
|
323
|
-
if (
|
|
324
|
-
return `${
|
|
2177
|
+
const text2 = (input || "").replace(/\s+/g, " ").trim();
|
|
2178
|
+
if (text2.length <= max) return text2;
|
|
2179
|
+
return `${text2.slice(0, max - 3)}...`;
|
|
325
2180
|
}
|
|
326
2181
|
function memoryGraphToMermaid(graph) {
|
|
327
2182
|
const lines = ["flowchart LR"];
|
|
@@ -359,36 +2214,42 @@ var DEFAULT_BASE_DELAY_MS = 250;
|
|
|
359
2214
|
var DEFAULT_MAX_DELAY_MS = 2e3;
|
|
360
2215
|
var DEFAULT_TIMEOUT_MS = 15e3;
|
|
361
2216
|
var PROJECT_CACHE_TTL_MS = 3e4;
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
2217
|
+
var DEPRECATION_WARNINGS2 = /* @__PURE__ */ new Set();
|
|
2218
|
+
function warnDeprecatedOnce2(key, message) {
|
|
2219
|
+
if (DEPRECATION_WARNINGS2.has(key)) return;
|
|
2220
|
+
DEPRECATION_WARNINGS2.add(key);
|
|
2221
|
+
if (typeof console !== "undefined" && typeof console.warn === "function") {
|
|
2222
|
+
console.warn(message);
|
|
2223
|
+
}
|
|
368
2224
|
}
|
|
369
2225
|
function isLikelyProjectId(projectRef) {
|
|
370
2226
|
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(projectRef);
|
|
371
2227
|
}
|
|
372
|
-
function
|
|
2228
|
+
function normalizeBaseUrl2(url) {
|
|
373
2229
|
let normalized = url.trim().replace(/\/+$/, "");
|
|
374
2230
|
normalized = normalized.replace(/\/api\/v1$/i, "");
|
|
375
2231
|
normalized = normalized.replace(/\/v1$/i, "");
|
|
376
2232
|
normalized = normalized.replace(/\/api$/i, "");
|
|
377
2233
|
return normalized;
|
|
378
2234
|
}
|
|
379
|
-
function
|
|
2235
|
+
function normalizeEndpoint2(endpoint) {
|
|
380
2236
|
const withLeadingSlash = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
|
|
381
2237
|
if (/^\/api\/v1(\/|$)/i.test(withLeadingSlash)) {
|
|
382
2238
|
return withLeadingSlash.replace(/^\/api/i, "");
|
|
383
2239
|
}
|
|
384
2240
|
return withLeadingSlash;
|
|
385
2241
|
}
|
|
2242
|
+
function isProjectNotFoundMessage(message) {
|
|
2243
|
+
const normalized = message.toLowerCase();
|
|
2244
|
+
return normalized.includes("project not found") || normalized.includes("no project found") || normalized.includes("project does not exist");
|
|
2245
|
+
}
|
|
386
2246
|
var WhisperContext = class _WhisperContext {
|
|
387
2247
|
apiKey;
|
|
388
2248
|
baseUrl;
|
|
389
2249
|
defaultProject;
|
|
390
2250
|
timeoutMs;
|
|
391
2251
|
retryConfig;
|
|
2252
|
+
runtimeClient;
|
|
392
2253
|
projectRefToId = /* @__PURE__ */ new Map();
|
|
393
2254
|
projectCache = [];
|
|
394
2255
|
projectCacheExpiresAt = 0;
|
|
@@ -400,7 +2261,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
400
2261
|
});
|
|
401
2262
|
}
|
|
402
2263
|
this.apiKey = config.apiKey;
|
|
403
|
-
this.baseUrl =
|
|
2264
|
+
this.baseUrl = normalizeBaseUrl2(config.baseUrl || "https://context.usewhisper.dev");
|
|
404
2265
|
this.defaultProject = config.project;
|
|
405
2266
|
this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
406
2267
|
this.retryConfig = {
|
|
@@ -408,6 +2269,35 @@ var WhisperContext = class _WhisperContext {
|
|
|
408
2269
|
baseDelayMs: config.retry?.baseDelayMs ?? DEFAULT_BASE_DELAY_MS,
|
|
409
2270
|
maxDelayMs: config.retry?.maxDelayMs ?? DEFAULT_MAX_DELAY_MS
|
|
410
2271
|
};
|
|
2272
|
+
this.runtimeClient = new RuntimeClient({
|
|
2273
|
+
apiKey: this.apiKey,
|
|
2274
|
+
baseUrl: this.baseUrl,
|
|
2275
|
+
compatMode: "fallback",
|
|
2276
|
+
timeouts: {
|
|
2277
|
+
searchMs: this.timeoutMs,
|
|
2278
|
+
writeAckMs: this.timeoutMs,
|
|
2279
|
+
bulkMs: Math.max(this.timeoutMs, 1e4),
|
|
2280
|
+
profileMs: this.timeoutMs,
|
|
2281
|
+
sessionMs: this.timeoutMs
|
|
2282
|
+
},
|
|
2283
|
+
retryPolicy: {
|
|
2284
|
+
baseBackoffMs: this.retryConfig.baseDelayMs,
|
|
2285
|
+
maxBackoffMs: this.retryConfig.maxDelayMs,
|
|
2286
|
+
maxAttemptsByOperation: {
|
|
2287
|
+
search: this.retryConfig.maxAttempts,
|
|
2288
|
+
writeAck: this.retryConfig.maxAttempts,
|
|
2289
|
+
bulk: this.retryConfig.maxAttempts,
|
|
2290
|
+
profile: this.retryConfig.maxAttempts,
|
|
2291
|
+
session: this.retryConfig.maxAttempts,
|
|
2292
|
+
query: this.retryConfig.maxAttempts,
|
|
2293
|
+
get: this.retryConfig.maxAttempts
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
});
|
|
2297
|
+
warnDeprecatedOnce2(
|
|
2298
|
+
"whisper_context_class",
|
|
2299
|
+
"[Whisper SDK] WhisperContext remains supported in v2 but is legacy. Prefer WhisperClient for runtime features (queue/cache/session/diagnostics)."
|
|
2300
|
+
);
|
|
411
2301
|
}
|
|
412
2302
|
withProject(project) {
|
|
413
2303
|
return new _WhisperContext({
|
|
@@ -489,9 +2379,17 @@ var WhisperContext = class _WhisperContext {
|
|
|
489
2379
|
return Array.from(candidates).filter(Boolean);
|
|
490
2380
|
}
|
|
491
2381
|
async withProjectRefFallback(projectRef, execute) {
|
|
2382
|
+
try {
|
|
2383
|
+
return await execute(projectRef);
|
|
2384
|
+
} catch (error) {
|
|
2385
|
+
if (!(error instanceof WhisperError) || error.code !== "PROJECT_NOT_FOUND") {
|
|
2386
|
+
throw error;
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
492
2389
|
const refs = await this.getProjectRefCandidates(projectRef);
|
|
493
2390
|
let lastError;
|
|
494
2391
|
for (const ref of refs) {
|
|
2392
|
+
if (ref === projectRef) continue;
|
|
495
2393
|
try {
|
|
496
2394
|
return await execute(ref);
|
|
497
2395
|
} catch (error) {
|
|
@@ -514,7 +2412,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
514
2412
|
if (status === 401 || /api key|unauthorized|forbidden/i.test(message)) {
|
|
515
2413
|
return { code: "INVALID_API_KEY", retryable: false };
|
|
516
2414
|
}
|
|
517
|
-
if (status === 404
|
|
2415
|
+
if (status === 404 && isProjectNotFoundMessage(message)) {
|
|
518
2416
|
return { code: "PROJECT_NOT_FOUND", retryable: false };
|
|
519
2417
|
}
|
|
520
2418
|
if (status === 408) {
|
|
@@ -528,72 +2426,68 @@ var WhisperContext = class _WhisperContext {
|
|
|
528
2426
|
}
|
|
529
2427
|
return { code: "REQUEST_FAILED", retryable: false };
|
|
530
2428
|
}
|
|
2429
|
+
isEndpointNotFoundError(error) {
|
|
2430
|
+
if (!(error instanceof WhisperError)) {
|
|
2431
|
+
return false;
|
|
2432
|
+
}
|
|
2433
|
+
if (error.status !== 404) {
|
|
2434
|
+
return false;
|
|
2435
|
+
}
|
|
2436
|
+
const message = (error.message || "").toLowerCase();
|
|
2437
|
+
return !isProjectNotFoundMessage(message);
|
|
2438
|
+
}
|
|
2439
|
+
inferOperation(endpoint, method) {
|
|
2440
|
+
const normalized = normalizeEndpoint2(endpoint).toLowerCase();
|
|
2441
|
+
if (normalized.includes("/memory/search")) return "search";
|
|
2442
|
+
if (normalized.includes("/memory/bulk")) return "bulk";
|
|
2443
|
+
if (normalized.includes("/memory/profile") || normalized.includes("/memory/session")) return "profile";
|
|
2444
|
+
if (normalized.includes("/memory/ingest/session")) return "session";
|
|
2445
|
+
if (normalized.includes("/context/query")) return "query";
|
|
2446
|
+
if (method === "GET") return "get";
|
|
2447
|
+
return "writeAck";
|
|
2448
|
+
}
|
|
531
2449
|
async request(endpoint, options = {}) {
|
|
532
|
-
const
|
|
533
|
-
const normalizedEndpoint =
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
2450
|
+
const method = String(options.method || "GET").toUpperCase();
|
|
2451
|
+
const normalizedEndpoint = normalizeEndpoint2(endpoint);
|
|
2452
|
+
const operation = this.inferOperation(normalizedEndpoint, method);
|
|
2453
|
+
let body;
|
|
2454
|
+
if (typeof options.body === "string") {
|
|
538
2455
|
try {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
};
|
|
543
|
-
const hasAuthHeader = Object.keys(headers).some((k) => k.toLowerCase() === "authorization");
|
|
544
|
-
const hasApiKeyHeader = Object.keys(headers).some((k) => k.toLowerCase() === "x-api-key");
|
|
545
|
-
if (!hasAuthHeader) {
|
|
546
|
-
headers.Authorization = `Bearer ${this.apiKey}`;
|
|
547
|
-
}
|
|
548
|
-
if (!hasApiKeyHeader) {
|
|
549
|
-
headers["X-API-Key"] = this.apiKey;
|
|
550
|
-
}
|
|
551
|
-
const response = await fetch(`${this.baseUrl}${normalizedEndpoint}`, {
|
|
552
|
-
...options,
|
|
553
|
-
signal: controller.signal,
|
|
554
|
-
headers
|
|
555
|
-
});
|
|
556
|
-
clearTimeout(timeout);
|
|
557
|
-
if (!response.ok) {
|
|
558
|
-
let payload = null;
|
|
559
|
-
try {
|
|
560
|
-
payload = await response.json();
|
|
561
|
-
} catch {
|
|
562
|
-
payload = await response.text().catch(() => "");
|
|
563
|
-
}
|
|
564
|
-
const message = typeof payload === "string" ? payload : payload?.error || payload?.message || `HTTP ${response.status}: ${response.statusText}`;
|
|
565
|
-
const { code, retryable } = this.classifyError(response.status, message);
|
|
566
|
-
const err = new WhisperError({
|
|
567
|
-
code,
|
|
568
|
-
message,
|
|
569
|
-
status: response.status,
|
|
570
|
-
retryable,
|
|
571
|
-
details: payload
|
|
572
|
-
});
|
|
573
|
-
if (!retryable || attempt === maxAttempts - 1) {
|
|
574
|
-
throw err;
|
|
575
|
-
}
|
|
576
|
-
await sleep(getBackoffDelay(attempt, this.retryConfig.baseDelayMs, this.retryConfig.maxDelayMs));
|
|
577
|
-
continue;
|
|
578
|
-
}
|
|
579
|
-
return response.json();
|
|
580
|
-
} catch (error) {
|
|
581
|
-
clearTimeout(timeout);
|
|
582
|
-
const isAbort = error?.name === "AbortError";
|
|
583
|
-
const mapped = error instanceof WhisperError ? error : new WhisperError({
|
|
584
|
-
code: isAbort ? "TIMEOUT" : "NETWORK_ERROR",
|
|
585
|
-
message: isAbort ? "Request timed out" : error?.message || "Network request failed",
|
|
586
|
-
retryable: true,
|
|
587
|
-
details: error
|
|
588
|
-
});
|
|
589
|
-
lastError = mapped;
|
|
590
|
-
if (!mapped.retryable || attempt === maxAttempts - 1) {
|
|
591
|
-
throw mapped;
|
|
592
|
-
}
|
|
593
|
-
await sleep(getBackoffDelay(attempt, this.retryConfig.baseDelayMs, this.retryConfig.maxDelayMs));
|
|
2456
|
+
body = JSON.parse(options.body);
|
|
2457
|
+
} catch {
|
|
2458
|
+
body = void 0;
|
|
594
2459
|
}
|
|
2460
|
+
} else if (options.body && typeof options.body === "object" && !ArrayBuffer.isView(options.body) && !(options.body instanceof ArrayBuffer) && !(options.body instanceof FormData) && !(options.body instanceof URLSearchParams) && !(options.body instanceof Blob) && !(options.body instanceof ReadableStream)) {
|
|
2461
|
+
body = options.body;
|
|
2462
|
+
}
|
|
2463
|
+
try {
|
|
2464
|
+
const response = await this.runtimeClient.request({
|
|
2465
|
+
endpoint: normalizedEndpoint,
|
|
2466
|
+
method,
|
|
2467
|
+
operation,
|
|
2468
|
+
idempotent: method === "GET" || method === "POST" && (operation === "search" || operation === "query" || operation === "profile"),
|
|
2469
|
+
body,
|
|
2470
|
+
headers: options.headers || {}
|
|
2471
|
+
});
|
|
2472
|
+
return response.data;
|
|
2473
|
+
} catch (error) {
|
|
2474
|
+
if (!(error instanceof RuntimeClientError)) {
|
|
2475
|
+
throw error;
|
|
2476
|
+
}
|
|
2477
|
+
let message = error.message;
|
|
2478
|
+
if (error.status === 404 && !isProjectNotFoundMessage(message)) {
|
|
2479
|
+
const endpointHint = `${this.baseUrl}${normalizedEndpoint}`;
|
|
2480
|
+
message = `Endpoint not found at ${endpointHint}. This deployment may not support this API route.`;
|
|
2481
|
+
}
|
|
2482
|
+
const { code, retryable } = this.classifyError(error.status, message);
|
|
2483
|
+
throw new WhisperError({
|
|
2484
|
+
code,
|
|
2485
|
+
message,
|
|
2486
|
+
status: error.status,
|
|
2487
|
+
retryable,
|
|
2488
|
+
details: error.details
|
|
2489
|
+
});
|
|
595
2490
|
}
|
|
596
|
-
throw lastError instanceof Error ? lastError : new WhisperError({ code: "REQUEST_FAILED", message: "Request failed" });
|
|
597
2491
|
}
|
|
598
2492
|
async query(params) {
|
|
599
2493
|
const projectRef = this.getRequiredProject(params.project);
|
|
@@ -666,7 +2560,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
666
2560
|
async addMemory(params) {
|
|
667
2561
|
const projectRef = this.getRequiredProject(params.project);
|
|
668
2562
|
return this.withProjectRefFallback(projectRef, async (project) => {
|
|
669
|
-
const
|
|
2563
|
+
const toSotaType2 = (memoryType) => {
|
|
670
2564
|
switch (memoryType) {
|
|
671
2565
|
case "episodic":
|
|
672
2566
|
return "event";
|
|
@@ -678,7 +2572,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
678
2572
|
return memoryType;
|
|
679
2573
|
}
|
|
680
2574
|
};
|
|
681
|
-
const
|
|
2575
|
+
const toLegacyType2 = (memoryType) => {
|
|
682
2576
|
switch (memoryType) {
|
|
683
2577
|
case "event":
|
|
684
2578
|
return "episodic";
|
|
@@ -699,18 +2593,23 @@ var WhisperContext = class _WhisperContext {
|
|
|
699
2593
|
body: JSON.stringify({
|
|
700
2594
|
project,
|
|
701
2595
|
content: params.content,
|
|
702
|
-
memory_type:
|
|
2596
|
+
memory_type: toSotaType2(params.memory_type),
|
|
703
2597
|
user_id: params.user_id,
|
|
704
2598
|
session_id: params.session_id,
|
|
705
2599
|
agent_id: params.agent_id,
|
|
706
2600
|
importance: params.importance,
|
|
707
|
-
metadata: params.metadata
|
|
2601
|
+
metadata: params.metadata,
|
|
2602
|
+
async: params.async,
|
|
2603
|
+
write_mode: params.write_mode
|
|
708
2604
|
})
|
|
709
2605
|
});
|
|
710
|
-
const id2 = direct?.memory?.id || direct?.id || direct?.memory_id;
|
|
2606
|
+
const id2 = direct?.memory?.id || direct?.id || direct?.memory_id || direct?.job_id;
|
|
711
2607
|
if (id2) {
|
|
712
2608
|
return { id: id2, success: true, path: "sota", fallback_used: false };
|
|
713
2609
|
}
|
|
2610
|
+
if (direct?.success === true) {
|
|
2611
|
+
return { id: "", success: true, path: "sota", fallback_used: false };
|
|
2612
|
+
}
|
|
714
2613
|
} catch (error) {
|
|
715
2614
|
if (params.allow_legacy_fallback === false) {
|
|
716
2615
|
throw error;
|
|
@@ -721,7 +2620,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
721
2620
|
body: JSON.stringify({
|
|
722
2621
|
project,
|
|
723
2622
|
content: params.content,
|
|
724
|
-
memory_type:
|
|
2623
|
+
memory_type: toLegacyType2(params.memory_type),
|
|
725
2624
|
user_id: params.user_id,
|
|
726
2625
|
session_id: params.session_id,
|
|
727
2626
|
agent_id: params.agent_id,
|
|
@@ -742,10 +2641,40 @@ var WhisperContext = class _WhisperContext {
|
|
|
742
2641
|
}
|
|
743
2642
|
async addMemoriesBulk(params) {
|
|
744
2643
|
const projectRef = this.getRequiredProject(params.project);
|
|
745
|
-
return this.withProjectRefFallback(projectRef, (project) =>
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
2644
|
+
return this.withProjectRefFallback(projectRef, async (project) => {
|
|
2645
|
+
try {
|
|
2646
|
+
return await this.request("/v1/memory/bulk", {
|
|
2647
|
+
method: "POST",
|
|
2648
|
+
body: JSON.stringify({ ...params, project })
|
|
2649
|
+
});
|
|
2650
|
+
} catch (error) {
|
|
2651
|
+
if (!this.isEndpointNotFoundError(error)) {
|
|
2652
|
+
throw error;
|
|
2653
|
+
}
|
|
2654
|
+
const created = await Promise.all(
|
|
2655
|
+
params.memories.map(
|
|
2656
|
+
(memory) => this.addMemory({
|
|
2657
|
+
project,
|
|
2658
|
+
content: memory.content,
|
|
2659
|
+
memory_type: memory.memory_type,
|
|
2660
|
+
user_id: memory.user_id,
|
|
2661
|
+
session_id: memory.session_id,
|
|
2662
|
+
agent_id: memory.agent_id,
|
|
2663
|
+
importance: memory.importance,
|
|
2664
|
+
metadata: memory.metadata,
|
|
2665
|
+
allow_legacy_fallback: true
|
|
2666
|
+
})
|
|
2667
|
+
)
|
|
2668
|
+
);
|
|
2669
|
+
return {
|
|
2670
|
+
success: true,
|
|
2671
|
+
created: created.length,
|
|
2672
|
+
memories: created,
|
|
2673
|
+
path: "legacy",
|
|
2674
|
+
fallback_used: true
|
|
2675
|
+
};
|
|
2676
|
+
}
|
|
2677
|
+
});
|
|
749
2678
|
}
|
|
750
2679
|
async extractMemories(params) {
|
|
751
2680
|
const projectRef = this.getRequiredProject(params.project);
|
|
@@ -770,17 +2699,48 @@ var WhisperContext = class _WhisperContext {
|
|
|
770
2699
|
}
|
|
771
2700
|
async searchMemories(params) {
|
|
772
2701
|
const projectRef = this.getRequiredProject(params.project);
|
|
773
|
-
return this.withProjectRefFallback(projectRef, (project) =>
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
2702
|
+
return this.withProjectRefFallback(projectRef, async (project) => {
|
|
2703
|
+
try {
|
|
2704
|
+
return await this.request("/v1/memory/search", {
|
|
2705
|
+
method: "POST",
|
|
2706
|
+
body: JSON.stringify({
|
|
2707
|
+
query: params.query,
|
|
2708
|
+
project,
|
|
2709
|
+
user_id: params.user_id,
|
|
2710
|
+
session_id: params.session_id,
|
|
2711
|
+
memory_types: params.memory_type ? [params.memory_type] : void 0,
|
|
2712
|
+
top_k: params.top_k || 10,
|
|
2713
|
+
profile: params.profile,
|
|
2714
|
+
include_pending: params.include_pending
|
|
2715
|
+
})
|
|
2716
|
+
});
|
|
2717
|
+
} catch (error) {
|
|
2718
|
+
if (!this.isEndpointNotFoundError(error)) {
|
|
2719
|
+
throw error;
|
|
2720
|
+
}
|
|
2721
|
+
const legacyTypeMap = {
|
|
2722
|
+
factual: "factual",
|
|
2723
|
+
preference: "semantic",
|
|
2724
|
+
event: "episodic",
|
|
2725
|
+
relationship: "semantic",
|
|
2726
|
+
opinion: "semantic",
|
|
2727
|
+
goal: "semantic",
|
|
2728
|
+
instruction: "procedural"
|
|
2729
|
+
};
|
|
2730
|
+
return this.request("/v1/memories/search", {
|
|
2731
|
+
method: "POST",
|
|
2732
|
+
body: JSON.stringify({
|
|
2733
|
+
query: params.query,
|
|
2734
|
+
project,
|
|
2735
|
+
user_id: params.user_id,
|
|
2736
|
+
session_id: params.session_id,
|
|
2737
|
+
agent_id: params.agent_id,
|
|
2738
|
+
memory_type: params.memory_type ? legacyTypeMap[params.memory_type] : void 0,
|
|
2739
|
+
top_k: params.top_k || 10
|
|
2740
|
+
})
|
|
2741
|
+
});
|
|
2742
|
+
}
|
|
2743
|
+
});
|
|
784
2744
|
}
|
|
785
2745
|
async createApiKey(params) {
|
|
786
2746
|
return this.request("/v1/keys", {
|
|
@@ -796,10 +2756,29 @@ var WhisperContext = class _WhisperContext {
|
|
|
796
2756
|
}
|
|
797
2757
|
async searchMemoriesSOTA(params) {
|
|
798
2758
|
const projectRef = this.getRequiredProject(params.project);
|
|
799
|
-
return this.withProjectRefFallback(projectRef, (project) =>
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
2759
|
+
return this.withProjectRefFallback(projectRef, async (project) => {
|
|
2760
|
+
try {
|
|
2761
|
+
return await this.request("/v1/memory/search", {
|
|
2762
|
+
method: "POST",
|
|
2763
|
+
body: JSON.stringify({ ...params, project })
|
|
2764
|
+
});
|
|
2765
|
+
} catch (error) {
|
|
2766
|
+
if (!this.isEndpointNotFoundError(error)) {
|
|
2767
|
+
throw error;
|
|
2768
|
+
}
|
|
2769
|
+
const firstType = params.memory_types?.[0];
|
|
2770
|
+
return this.searchMemories({
|
|
2771
|
+
project,
|
|
2772
|
+
query: params.query,
|
|
2773
|
+
user_id: params.user_id,
|
|
2774
|
+
session_id: params.session_id,
|
|
2775
|
+
memory_type: firstType,
|
|
2776
|
+
top_k: params.top_k,
|
|
2777
|
+
profile: params.profile,
|
|
2778
|
+
include_pending: params.include_pending
|
|
2779
|
+
});
|
|
2780
|
+
}
|
|
2781
|
+
});
|
|
803
2782
|
}
|
|
804
2783
|
async ingestSession(params) {
|
|
805
2784
|
const projectRef = this.getRequiredProject(params.project);
|
|
@@ -809,33 +2788,92 @@ var WhisperContext = class _WhisperContext {
|
|
|
809
2788
|
}));
|
|
810
2789
|
}
|
|
811
2790
|
async getSessionMemories(params) {
|
|
812
|
-
const
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
2791
|
+
const projectRef = this.getRequiredProject(params.project);
|
|
2792
|
+
return this.withProjectRefFallback(projectRef, async (project) => {
|
|
2793
|
+
const query = new URLSearchParams({
|
|
2794
|
+
project,
|
|
2795
|
+
...params.limit && { limit: params.limit.toString() },
|
|
2796
|
+
...params.since_date && { since_date: params.since_date },
|
|
2797
|
+
...params.include_pending !== void 0 && { include_pending: String(params.include_pending) }
|
|
2798
|
+
});
|
|
2799
|
+
try {
|
|
2800
|
+
return await this.request(`/v1/memory/session/${params.session_id}?${query}`);
|
|
2801
|
+
} catch (error) {
|
|
2802
|
+
if (!this.isEndpointNotFoundError(error)) {
|
|
2803
|
+
throw error;
|
|
2804
|
+
}
|
|
2805
|
+
return { memories: [], count: 0 };
|
|
2806
|
+
}
|
|
817
2807
|
});
|
|
818
|
-
return this.request(`/v1/memory/session/${params.session_id}?${query}`);
|
|
819
2808
|
}
|
|
820
2809
|
async getUserProfile(params) {
|
|
821
|
-
const
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
2810
|
+
const projectRef = this.getRequiredProject(params.project);
|
|
2811
|
+
return this.withProjectRefFallback(projectRef, async (project) => {
|
|
2812
|
+
const query = new URLSearchParams({
|
|
2813
|
+
project,
|
|
2814
|
+
...params.memory_types && { memory_types: params.memory_types },
|
|
2815
|
+
...params.include_pending !== void 0 && { include_pending: String(params.include_pending) }
|
|
2816
|
+
});
|
|
2817
|
+
try {
|
|
2818
|
+
return await this.request(`/v1/memory/profile/${params.user_id}?${query}`);
|
|
2819
|
+
} catch (error) {
|
|
2820
|
+
if (!this.isEndpointNotFoundError(error)) {
|
|
2821
|
+
throw error;
|
|
2822
|
+
}
|
|
2823
|
+
const legacyQuery = new URLSearchParams({
|
|
2824
|
+
project,
|
|
2825
|
+
user_id: params.user_id,
|
|
2826
|
+
limit: "200"
|
|
2827
|
+
});
|
|
2828
|
+
const legacy = await this.request(`/v1/memories?${legacyQuery}`);
|
|
2829
|
+
const memories = Array.isArray(legacy?.memories) ? legacy.memories : [];
|
|
2830
|
+
return {
|
|
2831
|
+
user_id: params.user_id,
|
|
2832
|
+
memories,
|
|
2833
|
+
count: memories.length
|
|
2834
|
+
};
|
|
2835
|
+
}
|
|
825
2836
|
});
|
|
826
|
-
return this.request(`/v1/memory/profile/${params.user_id}?${query}`);
|
|
827
2837
|
}
|
|
828
2838
|
async getMemoryVersions(memoryId) {
|
|
829
2839
|
return this.request(`/v1/memory/${memoryId}/versions`);
|
|
830
2840
|
}
|
|
831
2841
|
async updateMemory(memoryId, params) {
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
2842
|
+
try {
|
|
2843
|
+
return await this.request(`/v1/memory/${memoryId}`, {
|
|
2844
|
+
method: "PUT",
|
|
2845
|
+
body: JSON.stringify(params)
|
|
2846
|
+
});
|
|
2847
|
+
} catch (error) {
|
|
2848
|
+
if (!this.isEndpointNotFoundError(error)) {
|
|
2849
|
+
throw error;
|
|
2850
|
+
}
|
|
2851
|
+
const legacy = await this.request(`/v1/memories/${memoryId}`, {
|
|
2852
|
+
method: "PUT",
|
|
2853
|
+
body: JSON.stringify({
|
|
2854
|
+
content: params.content
|
|
2855
|
+
})
|
|
2856
|
+
});
|
|
2857
|
+
return {
|
|
2858
|
+
success: true,
|
|
2859
|
+
new_memory_id: legacy?.id || memoryId,
|
|
2860
|
+
old_memory_id: memoryId
|
|
2861
|
+
};
|
|
2862
|
+
}
|
|
836
2863
|
}
|
|
837
2864
|
async deleteMemory(memoryId) {
|
|
838
|
-
|
|
2865
|
+
try {
|
|
2866
|
+
return await this.request(`/v1/memory/${memoryId}`, { method: "DELETE" });
|
|
2867
|
+
} catch (error) {
|
|
2868
|
+
if (!this.isEndpointNotFoundError(error)) {
|
|
2869
|
+
throw error;
|
|
2870
|
+
}
|
|
2871
|
+
await this.request(`/v1/memories/${memoryId}`, { method: "DELETE" });
|
|
2872
|
+
return {
|
|
2873
|
+
success: true,
|
|
2874
|
+
deleted: memoryId
|
|
2875
|
+
};
|
|
2876
|
+
}
|
|
839
2877
|
}
|
|
840
2878
|
async getMemoryRelations(memoryId) {
|
|
841
2879
|
return this.request(`/v1/memory/${memoryId}/relations`);
|
|
@@ -1026,12 +3064,18 @@ var WhisperContext = class _WhisperContext {
|
|
|
1026
3064
|
};
|
|
1027
3065
|
var index_default = WhisperContext;
|
|
1028
3066
|
export {
|
|
3067
|
+
LangChainMemoryAdapter,
|
|
3068
|
+
LangGraphCheckpointAdapter,
|
|
1029
3069
|
Whisper,
|
|
1030
3070
|
WhisperAgentMiddleware,
|
|
3071
|
+
WhisperClient,
|
|
1031
3072
|
WhisperContext,
|
|
1032
3073
|
whisper_agent_default as WhisperDefault,
|
|
1033
3074
|
WhisperError,
|
|
3075
|
+
whisper_default as WhisperRuntimeClient,
|
|
1034
3076
|
createAgentMiddleware,
|
|
3077
|
+
createLangChainMemoryAdapter,
|
|
3078
|
+
createLangGraphCheckpointAdapter,
|
|
1035
3079
|
index_default as default,
|
|
1036
3080
|
memoryGraphToMermaid
|
|
1037
3081
|
};
|