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