@usewhisper/sdk 2.2.1 → 3.1.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/README.md +2 -2
- package/index.d.mts +753 -5
- package/index.d.ts +753 -5
- package/index.js +2126 -148
- package/index.mjs +2110 -148
- package/package.json +2 -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,424 @@ 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_API_KEY_ONLY_PREFIXES = ["/v1/memory", "/v1/context/query"];
|
|
138
|
+
var DEFAULT_RETRY_ATTEMPTS = {
|
|
139
|
+
search: 3,
|
|
140
|
+
writeAck: 2,
|
|
141
|
+
bulk: 2,
|
|
142
|
+
profile: 2,
|
|
143
|
+
session: 2,
|
|
144
|
+
query: 3,
|
|
145
|
+
get: 2
|
|
146
|
+
};
|
|
147
|
+
function isObject(value) {
|
|
148
|
+
return typeof value === "object" && value !== null;
|
|
149
|
+
}
|
|
150
|
+
function toMessage(payload, status, statusText) {
|
|
151
|
+
if (typeof payload === "string" && payload.trim()) return payload;
|
|
152
|
+
if (isObject(payload)) {
|
|
153
|
+
const maybeError = payload.error;
|
|
154
|
+
const maybeMessage = payload.message;
|
|
155
|
+
if (typeof maybeError === "string" && maybeError.trim()) return maybeError;
|
|
156
|
+
if (typeof maybeMessage === "string" && maybeMessage.trim()) return maybeMessage;
|
|
157
|
+
if (isObject(maybeError) && typeof maybeError.message === "string") return maybeError.message;
|
|
158
|
+
}
|
|
159
|
+
return `HTTP ${status}: ${statusText}`;
|
|
160
|
+
}
|
|
161
|
+
var RuntimeClientError = class extends Error {
|
|
162
|
+
status;
|
|
163
|
+
retryable;
|
|
164
|
+
code;
|
|
165
|
+
details;
|
|
166
|
+
traceId;
|
|
167
|
+
constructor(args) {
|
|
168
|
+
super(args.message);
|
|
169
|
+
this.name = "RuntimeClientError";
|
|
170
|
+
this.status = args.status;
|
|
171
|
+
this.retryable = args.retryable;
|
|
172
|
+
this.code = args.code;
|
|
173
|
+
this.details = args.details;
|
|
174
|
+
this.traceId = args.traceId;
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
var RuntimeClient = class {
|
|
178
|
+
apiKey;
|
|
179
|
+
baseUrl;
|
|
180
|
+
sdkVersion;
|
|
181
|
+
compatMode;
|
|
182
|
+
retryPolicy;
|
|
183
|
+
timeouts;
|
|
184
|
+
diagnostics;
|
|
185
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
186
|
+
sendApiKeyHeader;
|
|
187
|
+
constructor(options, diagnostics) {
|
|
188
|
+
if (!options.apiKey) {
|
|
189
|
+
throw new RuntimeClientError({
|
|
190
|
+
code: "INVALID_API_KEY",
|
|
191
|
+
message: "API key is required",
|
|
192
|
+
retryable: false
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
this.apiKey = options.apiKey;
|
|
196
|
+
this.baseUrl = normalizeBaseUrl(options.baseUrl || "https://context.usewhisper.dev");
|
|
197
|
+
this.sdkVersion = options.sdkVersion || "2.x-runtime";
|
|
198
|
+
this.compatMode = options.compatMode || "fallback";
|
|
199
|
+
this.retryPolicy = {
|
|
200
|
+
retryableStatusCodes: options.retryPolicy?.retryableStatusCodes || DEFAULT_RETRYABLE_STATUS,
|
|
201
|
+
retryOnNetworkError: options.retryPolicy?.retryOnNetworkError ?? true,
|
|
202
|
+
maxBackoffMs: options.retryPolicy?.maxBackoffMs ?? 1200,
|
|
203
|
+
baseBackoffMs: options.retryPolicy?.baseBackoffMs ?? 250,
|
|
204
|
+
maxAttemptsByOperation: options.retryPolicy?.maxAttemptsByOperation || {}
|
|
205
|
+
};
|
|
206
|
+
this.timeouts = {
|
|
207
|
+
...DEFAULT_TIMEOUTS,
|
|
208
|
+
...options.timeouts || {}
|
|
209
|
+
};
|
|
210
|
+
this.sendApiKeyHeader = process.env.WHISPER_SEND_X_API_KEY === "1";
|
|
211
|
+
this.diagnostics = diagnostics || new DiagnosticsStore(1e3);
|
|
212
|
+
}
|
|
213
|
+
getDiagnosticsStore() {
|
|
214
|
+
return this.diagnostics;
|
|
215
|
+
}
|
|
216
|
+
getCompatMode() {
|
|
217
|
+
return this.compatMode;
|
|
218
|
+
}
|
|
219
|
+
timeoutFor(operation) {
|
|
220
|
+
switch (operation) {
|
|
221
|
+
case "search":
|
|
222
|
+
return this.timeouts.searchMs;
|
|
223
|
+
case "writeAck":
|
|
224
|
+
return this.timeouts.writeAckMs;
|
|
225
|
+
case "bulk":
|
|
226
|
+
return this.timeouts.bulkMs;
|
|
227
|
+
case "profile":
|
|
228
|
+
return this.timeouts.profileMs;
|
|
229
|
+
case "session":
|
|
230
|
+
return this.timeouts.sessionMs;
|
|
231
|
+
case "query":
|
|
232
|
+
case "get":
|
|
233
|
+
default:
|
|
234
|
+
return this.timeouts.searchMs;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
maxAttemptsFor(operation) {
|
|
238
|
+
const override = this.retryPolicy.maxAttemptsByOperation?.[operation];
|
|
239
|
+
return Math.max(1, override ?? DEFAULT_RETRY_ATTEMPTS[operation]);
|
|
240
|
+
}
|
|
241
|
+
shouldRetryStatus(status) {
|
|
242
|
+
return status !== void 0 && this.retryPolicy.retryableStatusCodes?.includes(status) === true;
|
|
243
|
+
}
|
|
244
|
+
backoff(attempt) {
|
|
245
|
+
const base = this.retryPolicy.baseBackoffMs ?? 250;
|
|
246
|
+
const max = this.retryPolicy.maxBackoffMs ?? 1200;
|
|
247
|
+
const jitter = 0.8 + Math.random() * 0.4;
|
|
248
|
+
return Math.min(max, Math.floor(base * Math.pow(2, attempt) * jitter));
|
|
249
|
+
}
|
|
250
|
+
runtimeName() {
|
|
251
|
+
const maybeWindow = globalThis.window;
|
|
252
|
+
return maybeWindow && typeof maybeWindow === "object" ? "browser" : "node";
|
|
253
|
+
}
|
|
254
|
+
apiKeyOnlyPrefixes() {
|
|
255
|
+
const raw = process.env.WHISPER_API_KEY_ONLY_PREFIXES;
|
|
256
|
+
if (!raw || !raw.trim()) return DEFAULT_API_KEY_ONLY_PREFIXES;
|
|
257
|
+
return raw.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
|
|
258
|
+
}
|
|
259
|
+
shouldAttachApiKeyHeader(endpoint) {
|
|
260
|
+
if (this.sendApiKeyHeader) return true;
|
|
261
|
+
const prefixes = this.apiKeyOnlyPrefixes();
|
|
262
|
+
return prefixes.some((prefix) => endpoint === prefix || endpoint.startsWith(`${prefix}/`));
|
|
263
|
+
}
|
|
264
|
+
createRequestFingerprint(options) {
|
|
265
|
+
const normalizedEndpoint = normalizeEndpoint(options.endpoint);
|
|
266
|
+
const authFingerprint = stableHash(this.apiKey.replace(/^Bearer\s+/i, ""));
|
|
267
|
+
const payload = JSON.stringify({
|
|
268
|
+
method: options.method || "GET",
|
|
269
|
+
endpoint: normalizedEndpoint,
|
|
270
|
+
body: options.body || null,
|
|
271
|
+
extra: options.dedupeKeyExtra || "",
|
|
272
|
+
authFingerprint
|
|
273
|
+
});
|
|
274
|
+
return stableHash(payload);
|
|
275
|
+
}
|
|
276
|
+
async request(options) {
|
|
277
|
+
const dedupeKey = options.idempotent ? this.createRequestFingerprint(options) : null;
|
|
278
|
+
if (dedupeKey) {
|
|
279
|
+
const inFlight = this.inFlight.get(dedupeKey);
|
|
280
|
+
if (inFlight) {
|
|
281
|
+
const data = await inFlight;
|
|
282
|
+
this.diagnostics.add({
|
|
283
|
+
id: randomId("diag"),
|
|
284
|
+
startedAt: nowIso(),
|
|
285
|
+
endedAt: nowIso(),
|
|
286
|
+
traceId: data.traceId,
|
|
287
|
+
spanId: randomId("span"),
|
|
288
|
+
operation: options.operation,
|
|
289
|
+
method: options.method || "GET",
|
|
290
|
+
endpoint: normalizeEndpoint(options.endpoint),
|
|
291
|
+
status: data.status,
|
|
292
|
+
durationMs: 0,
|
|
293
|
+
success: true,
|
|
294
|
+
deduped: true
|
|
295
|
+
});
|
|
296
|
+
const cloned = {
|
|
297
|
+
data: data.data,
|
|
298
|
+
status: data.status,
|
|
299
|
+
traceId: data.traceId
|
|
300
|
+
};
|
|
301
|
+
return cloned;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
const runner = this.performRequest(options).then((data) => {
|
|
305
|
+
if (dedupeKey) this.inFlight.delete(dedupeKey);
|
|
306
|
+
return data;
|
|
307
|
+
}).catch((error) => {
|
|
308
|
+
if (dedupeKey) this.inFlight.delete(dedupeKey);
|
|
309
|
+
throw error;
|
|
310
|
+
});
|
|
311
|
+
if (dedupeKey) {
|
|
312
|
+
this.inFlight.set(dedupeKey, runner);
|
|
313
|
+
}
|
|
314
|
+
return runner;
|
|
315
|
+
}
|
|
316
|
+
async performRequest(options) {
|
|
317
|
+
const method = options.method || "GET";
|
|
318
|
+
const normalizedEndpoint = normalizeEndpoint(options.endpoint);
|
|
319
|
+
const operation = options.operation;
|
|
320
|
+
const maxAttempts = this.maxAttemptsFor(operation);
|
|
321
|
+
const timeoutMs = this.timeoutFor(operation);
|
|
322
|
+
const traceId = options.traceId || randomId("trace");
|
|
323
|
+
let lastError = null;
|
|
324
|
+
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
325
|
+
const spanId = randomId("span");
|
|
326
|
+
const startedAt = Date.now();
|
|
327
|
+
const controller = new AbortController();
|
|
328
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
329
|
+
try {
|
|
330
|
+
const attachApiKeyHeader = this.shouldAttachApiKeyHeader(normalizedEndpoint);
|
|
331
|
+
const response = await fetch(`${this.baseUrl}${normalizedEndpoint}`, {
|
|
332
|
+
method,
|
|
333
|
+
signal: controller.signal,
|
|
334
|
+
keepalive: method !== "GET",
|
|
335
|
+
headers: {
|
|
336
|
+
"Content-Type": "application/json",
|
|
337
|
+
Authorization: this.apiKey.startsWith("Bearer ") ? this.apiKey : `Bearer ${this.apiKey}`,
|
|
338
|
+
...attachApiKeyHeader ? { "X-API-Key": this.apiKey.replace(/^Bearer\s+/i, "") } : {},
|
|
339
|
+
"x-trace-id": traceId,
|
|
340
|
+
"x-span-id": spanId,
|
|
341
|
+
"x-sdk-version": this.sdkVersion,
|
|
342
|
+
"x-sdk-runtime": this.runtimeName(),
|
|
343
|
+
...options.headers || {}
|
|
344
|
+
},
|
|
345
|
+
body: method === "GET" || method === "DELETE" ? void 0 : JSON.stringify(options.body || {})
|
|
346
|
+
});
|
|
347
|
+
clearTimeout(timeout);
|
|
348
|
+
let payload = null;
|
|
349
|
+
try {
|
|
350
|
+
payload = await response.json();
|
|
351
|
+
} catch {
|
|
352
|
+
payload = await response.text().catch(() => "");
|
|
353
|
+
}
|
|
354
|
+
const durationMs = Date.now() - startedAt;
|
|
355
|
+
const record = {
|
|
356
|
+
id: randomId("diag"),
|
|
357
|
+
startedAt: new Date(startedAt).toISOString(),
|
|
358
|
+
endedAt: nowIso(),
|
|
359
|
+
traceId,
|
|
360
|
+
spanId,
|
|
361
|
+
operation,
|
|
362
|
+
method,
|
|
363
|
+
endpoint: normalizedEndpoint,
|
|
364
|
+
status: response.status,
|
|
365
|
+
durationMs,
|
|
366
|
+
success: response.ok
|
|
367
|
+
};
|
|
368
|
+
this.diagnostics.add(record);
|
|
369
|
+
if (response.ok) {
|
|
370
|
+
return {
|
|
371
|
+
data: payload,
|
|
372
|
+
status: response.status,
|
|
373
|
+
traceId
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
const message = toMessage(payload, response.status, response.statusText);
|
|
377
|
+
const retryable = this.shouldRetryStatus(response.status);
|
|
378
|
+
const error = new RuntimeClientError({
|
|
379
|
+
message,
|
|
380
|
+
status: response.status,
|
|
381
|
+
retryable,
|
|
382
|
+
code: response.status === 404 ? "NOT_FOUND" : "REQUEST_FAILED",
|
|
383
|
+
details: payload,
|
|
384
|
+
traceId
|
|
385
|
+
});
|
|
386
|
+
lastError = error;
|
|
387
|
+
if (!retryable || attempt === maxAttempts - 1) {
|
|
388
|
+
throw error;
|
|
389
|
+
}
|
|
390
|
+
} catch (error) {
|
|
391
|
+
clearTimeout(timeout);
|
|
392
|
+
const durationMs = Date.now() - startedAt;
|
|
393
|
+
const isAbort = isObject(error) && error.name === "AbortError";
|
|
394
|
+
const mapped = error instanceof RuntimeClientError ? error : new RuntimeClientError({
|
|
395
|
+
message: isAbort ? "Request timed out" : error instanceof Error ? error.message : "Network error",
|
|
396
|
+
retryable: this.retryPolicy.retryOnNetworkError ?? true,
|
|
397
|
+
code: isAbort ? "TIMEOUT" : "NETWORK_ERROR",
|
|
398
|
+
traceId
|
|
399
|
+
});
|
|
400
|
+
lastError = mapped;
|
|
401
|
+
this.diagnostics.add({
|
|
402
|
+
id: randomId("diag"),
|
|
403
|
+
startedAt: new Date(startedAt).toISOString(),
|
|
404
|
+
endedAt: nowIso(),
|
|
405
|
+
traceId,
|
|
406
|
+
spanId,
|
|
407
|
+
operation,
|
|
408
|
+
method,
|
|
409
|
+
endpoint: normalizedEndpoint,
|
|
410
|
+
durationMs,
|
|
411
|
+
success: false,
|
|
412
|
+
errorCode: mapped.code,
|
|
413
|
+
errorMessage: mapped.message
|
|
414
|
+
});
|
|
415
|
+
if (!mapped.retryable || attempt === maxAttempts - 1) {
|
|
416
|
+
throw mapped;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
await new Promise((resolve) => setTimeout(resolve, this.backoff(attempt)));
|
|
420
|
+
}
|
|
421
|
+
throw lastError || new RuntimeClientError({
|
|
422
|
+
message: "Request failed",
|
|
423
|
+
retryable: false,
|
|
424
|
+
code: "REQUEST_FAILED"
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
|
|
34
429
|
// ../src/sdk/whisper-agent.ts
|
|
430
|
+
var DEPRECATION_WARNINGS = /* @__PURE__ */ new Set();
|
|
431
|
+
function warnDeprecatedOnce(key, message) {
|
|
432
|
+
if (DEPRECATION_WARNINGS.has(key)) return;
|
|
433
|
+
DEPRECATION_WARNINGS.add(key);
|
|
434
|
+
if (typeof console !== "undefined" && typeof console.warn === "function") {
|
|
435
|
+
console.warn(message);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
35
438
|
var Whisper = class {
|
|
36
439
|
client;
|
|
37
440
|
options;
|
|
@@ -49,6 +452,10 @@ var Whisper = class {
|
|
|
49
452
|
if (options.timeoutMs) clientConfig.timeoutMs = options.timeoutMs;
|
|
50
453
|
if (options.retry) clientConfig.retry = options.retry;
|
|
51
454
|
this.client = new WhisperContext(clientConfig);
|
|
455
|
+
warnDeprecatedOnce(
|
|
456
|
+
"whisper_agent_wrapper",
|
|
457
|
+
"[Whisper SDK] Whisper wrapper is supported for v2 compatibility. Prefer WhisperClient for new integrations."
|
|
458
|
+
);
|
|
52
459
|
const finalRetry = options.retry || { maxAttempts: 3, baseDelayMs: 250, maxDelayMs: 2e3 };
|
|
53
460
|
this.options = {
|
|
54
461
|
apiKey: options.apiKey,
|
|
@@ -226,83 +633,1163 @@ ${context}` : "",
|
|
|
226
633
|
return { success: false, extracted: 0 };
|
|
227
634
|
}
|
|
228
635
|
}
|
|
229
|
-
/**
|
|
230
|
-
* Run a full agent turn with automatic memory read (before) + write (after).
|
|
231
|
-
*/
|
|
232
|
-
async runTurn(params) {
|
|
233
|
-
const contextResult = await this.getContext(params.userMessage, {
|
|
636
|
+
/**
|
|
637
|
+
* Run a full agent turn with automatic memory read (before) + write (after).
|
|
638
|
+
*/
|
|
639
|
+
async runTurn(params) {
|
|
640
|
+
const contextResult = await this.getContext(params.userMessage, {
|
|
641
|
+
userId: params.userId,
|
|
642
|
+
sessionId: params.sessionId,
|
|
643
|
+
project: params.project,
|
|
644
|
+
limit: params.limit
|
|
645
|
+
});
|
|
646
|
+
const prompt = contextResult.context ? `${contextResult.context}
|
|
647
|
+
|
|
648
|
+
User: ${params.userMessage}` : params.userMessage;
|
|
649
|
+
const response = await params.generate(prompt);
|
|
650
|
+
const captureResult = await this.captureSession(
|
|
651
|
+
[
|
|
652
|
+
{ role: "user", content: params.userMessage },
|
|
653
|
+
{ role: "assistant", content: response }
|
|
654
|
+
],
|
|
655
|
+
{
|
|
656
|
+
userId: params.userId,
|
|
657
|
+
sessionId: params.sessionId,
|
|
658
|
+
project: params.project
|
|
659
|
+
}
|
|
660
|
+
);
|
|
661
|
+
return {
|
|
662
|
+
response,
|
|
663
|
+
context: contextResult.context,
|
|
664
|
+
count: contextResult.count,
|
|
665
|
+
extracted: captureResult.extracted
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* Direct access to WhisperContext for advanced usage
|
|
670
|
+
*/
|
|
671
|
+
raw() {
|
|
672
|
+
return this.client;
|
|
673
|
+
}
|
|
674
|
+
extractMemoryIdsFromBulkResponse(bulkResponse) {
|
|
675
|
+
const ids = [];
|
|
676
|
+
if (Array.isArray(bulkResponse?.memories)) {
|
|
677
|
+
for (const memory of bulkResponse.memories) {
|
|
678
|
+
if (memory?.id) ids.push(memory.id);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
if (bulkResponse?.memory?.id) {
|
|
682
|
+
ids.push(bulkResponse.memory.id);
|
|
683
|
+
}
|
|
684
|
+
if (bulkResponse?.id) {
|
|
685
|
+
ids.push(bulkResponse.id);
|
|
686
|
+
}
|
|
687
|
+
return Array.from(new Set(ids));
|
|
688
|
+
}
|
|
689
|
+
async fallbackCaptureViaAddMemory(messages, options) {
|
|
690
|
+
const userMessages = messages.filter((m) => m.role === "user").map((m) => (m.content || "").trim()).filter((content) => content.length >= 5).slice(-2);
|
|
691
|
+
if (userMessages.length === 0) {
|
|
692
|
+
return { success: false, extracted: 0 };
|
|
693
|
+
}
|
|
694
|
+
let extracted = 0;
|
|
695
|
+
for (const content of userMessages) {
|
|
696
|
+
try {
|
|
697
|
+
await this.client.addMemory({
|
|
698
|
+
project: options?.project ?? this.options.project,
|
|
699
|
+
content,
|
|
700
|
+
memory_type: "factual",
|
|
701
|
+
user_id: options?.userId ?? this.userId,
|
|
702
|
+
session_id: options?.sessionId ?? this.sessionId,
|
|
703
|
+
allow_legacy_fallback: true
|
|
704
|
+
});
|
|
705
|
+
extracted += 1;
|
|
706
|
+
} catch {
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
return { success: extracted > 0, extracted };
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
var whisper_agent_default = Whisper;
|
|
713
|
+
|
|
714
|
+
// ../src/sdk/core/cache.ts
|
|
715
|
+
var SearchResponseCache = class {
|
|
716
|
+
ttlMs;
|
|
717
|
+
capacity;
|
|
718
|
+
byKey = /* @__PURE__ */ new Map();
|
|
719
|
+
scopeIndex = /* @__PURE__ */ new Map();
|
|
720
|
+
constructor(ttlMs = 7e3, capacity = 500) {
|
|
721
|
+
this.ttlMs = Math.max(1e3, ttlMs);
|
|
722
|
+
this.capacity = Math.max(10, capacity);
|
|
723
|
+
}
|
|
724
|
+
makeScopeKey(project, userId, sessionId) {
|
|
725
|
+
return `${project}:${userId || "_"}:${sessionId || "_"}`;
|
|
726
|
+
}
|
|
727
|
+
makeKey(input) {
|
|
728
|
+
const normalized = {
|
|
729
|
+
project: input.project,
|
|
730
|
+
userId: input.userId || "",
|
|
731
|
+
sessionId: input.sessionId || "",
|
|
732
|
+
query: normalizeQuery(input.query),
|
|
733
|
+
topK: input.topK,
|
|
734
|
+
profile: input.profile,
|
|
735
|
+
includePending: input.includePending
|
|
736
|
+
};
|
|
737
|
+
return `search:${stableHash(JSON.stringify(normalized))}`;
|
|
738
|
+
}
|
|
739
|
+
get(key) {
|
|
740
|
+
const found = this.byKey.get(key);
|
|
741
|
+
if (!found) return null;
|
|
742
|
+
if (found.expiresAt <= Date.now()) {
|
|
743
|
+
this.deleteByKey(key);
|
|
744
|
+
return null;
|
|
745
|
+
}
|
|
746
|
+
found.touchedAt = Date.now();
|
|
747
|
+
return found.value;
|
|
748
|
+
}
|
|
749
|
+
set(key, scopeKey, value) {
|
|
750
|
+
this.byKey.set(key, {
|
|
751
|
+
value,
|
|
752
|
+
scopeKey,
|
|
753
|
+
touchedAt: Date.now(),
|
|
754
|
+
expiresAt: Date.now() + this.ttlMs
|
|
755
|
+
});
|
|
756
|
+
if (!this.scopeIndex.has(scopeKey)) {
|
|
757
|
+
this.scopeIndex.set(scopeKey, /* @__PURE__ */ new Set());
|
|
758
|
+
}
|
|
759
|
+
this.scopeIndex.get(scopeKey).add(key);
|
|
760
|
+
this.evictIfNeeded();
|
|
761
|
+
}
|
|
762
|
+
invalidateScope(scopeKey) {
|
|
763
|
+
const keys = this.scopeIndex.get(scopeKey);
|
|
764
|
+
if (!keys || keys.size === 0) {
|
|
765
|
+
return 0;
|
|
766
|
+
}
|
|
767
|
+
const toDelete = Array.from(keys);
|
|
768
|
+
for (const key of toDelete) {
|
|
769
|
+
this.deleteByKey(key);
|
|
770
|
+
}
|
|
771
|
+
this.scopeIndex.delete(scopeKey);
|
|
772
|
+
return toDelete.length;
|
|
773
|
+
}
|
|
774
|
+
evictIfNeeded() {
|
|
775
|
+
if (this.byKey.size <= this.capacity) return;
|
|
776
|
+
const ordered = Array.from(this.byKey.entries()).sort((a, b) => a[1].touchedAt - b[1].touchedAt);
|
|
777
|
+
const removeCount = this.byKey.size - this.capacity;
|
|
778
|
+
for (let i = 0; i < removeCount; i += 1) {
|
|
779
|
+
this.deleteByKey(ordered[i][0]);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
deleteByKey(key) {
|
|
783
|
+
const found = this.byKey.get(key);
|
|
784
|
+
if (!found) return;
|
|
785
|
+
this.byKey.delete(key);
|
|
786
|
+
const scopeKeys = this.scopeIndex.get(found.scopeKey);
|
|
787
|
+
if (!scopeKeys) return;
|
|
788
|
+
scopeKeys.delete(key);
|
|
789
|
+
if (scopeKeys.size === 0) {
|
|
790
|
+
this.scopeIndex.delete(found.scopeKey);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
|
|
795
|
+
// ../src/sdk/core/queue.ts
|
|
796
|
+
var InMemoryQueueStore = class {
|
|
797
|
+
items = [];
|
|
798
|
+
async load() {
|
|
799
|
+
return [...this.items];
|
|
800
|
+
}
|
|
801
|
+
async save(items) {
|
|
802
|
+
this.items = [...items];
|
|
803
|
+
}
|
|
804
|
+
};
|
|
805
|
+
var WriteQueue = class {
|
|
806
|
+
flushHandler;
|
|
807
|
+
store;
|
|
808
|
+
maxBatchSize;
|
|
809
|
+
flushIntervalMs;
|
|
810
|
+
maxAttempts;
|
|
811
|
+
queue = [];
|
|
812
|
+
flushTimer = null;
|
|
813
|
+
flushing = false;
|
|
814
|
+
lastFlushAt;
|
|
815
|
+
lastFlushCount = 0;
|
|
816
|
+
constructor(args) {
|
|
817
|
+
this.flushHandler = args.flushHandler;
|
|
818
|
+
this.store = args.store || new InMemoryQueueStore();
|
|
819
|
+
this.maxBatchSize = Math.max(1, args.maxBatchSize ?? 50);
|
|
820
|
+
this.flushIntervalMs = Math.max(10, args.flushIntervalMs ?? 100);
|
|
821
|
+
this.maxAttempts = Math.max(1, args.maxAttempts ?? 2);
|
|
822
|
+
}
|
|
823
|
+
async start() {
|
|
824
|
+
const pending = await this.store.load();
|
|
825
|
+
if (pending.length > 0) {
|
|
826
|
+
this.queue.push(...pending);
|
|
827
|
+
}
|
|
828
|
+
if (!this.flushTimer) {
|
|
829
|
+
this.flushTimer = setInterval(() => {
|
|
830
|
+
void this.flush();
|
|
831
|
+
}, this.flushIntervalMs);
|
|
832
|
+
const timer = this.flushTimer;
|
|
833
|
+
if (typeof timer.unref === "function") {
|
|
834
|
+
timer.unref();
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
this.bindProcessHooks();
|
|
838
|
+
}
|
|
839
|
+
async stop() {
|
|
840
|
+
if (this.flushTimer) {
|
|
841
|
+
clearInterval(this.flushTimer);
|
|
842
|
+
this.flushTimer = null;
|
|
843
|
+
}
|
|
844
|
+
await this.flush();
|
|
845
|
+
}
|
|
846
|
+
status() {
|
|
847
|
+
return {
|
|
848
|
+
queued: this.queue.length,
|
|
849
|
+
flushing: this.flushing,
|
|
850
|
+
lastFlushAt: this.lastFlushAt,
|
|
851
|
+
lastFlushCount: this.lastFlushCount
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
async enqueue(input) {
|
|
855
|
+
const eventId = input.eventId || this.makeEventId(input);
|
|
856
|
+
const item = {
|
|
857
|
+
...input,
|
|
858
|
+
eventId,
|
|
859
|
+
createdAt: nowIso()
|
|
860
|
+
};
|
|
861
|
+
this.queue.push(item);
|
|
862
|
+
await this.store.save(this.queue);
|
|
863
|
+
if (this.queue.length >= this.maxBatchSize) {
|
|
864
|
+
void this.flush();
|
|
865
|
+
}
|
|
866
|
+
return item;
|
|
867
|
+
}
|
|
868
|
+
async flush() {
|
|
869
|
+
if (this.flushing || this.queue.length === 0) return;
|
|
870
|
+
this.flushing = true;
|
|
871
|
+
try {
|
|
872
|
+
while (this.queue.length > 0) {
|
|
873
|
+
const batch = this.queue.slice(0, this.maxBatchSize);
|
|
874
|
+
let done = false;
|
|
875
|
+
let error = null;
|
|
876
|
+
for (let attempt = 0; attempt < this.maxAttempts; attempt += 1) {
|
|
877
|
+
try {
|
|
878
|
+
await this.flushHandler(batch);
|
|
879
|
+
done = true;
|
|
880
|
+
break;
|
|
881
|
+
} catch (err) {
|
|
882
|
+
error = err;
|
|
883
|
+
await new Promise((resolve) => setTimeout(resolve, (attempt + 1) * 180));
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
if (!done) {
|
|
887
|
+
throw error instanceof Error ? error : new Error("Queue flush failed");
|
|
888
|
+
}
|
|
889
|
+
this.queue.splice(0, batch.length);
|
|
890
|
+
this.lastFlushAt = nowIso();
|
|
891
|
+
this.lastFlushCount = batch.length;
|
|
892
|
+
await this.store.save(this.queue);
|
|
893
|
+
}
|
|
894
|
+
} finally {
|
|
895
|
+
this.flushing = false;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
makeEventId(input) {
|
|
899
|
+
const source = JSON.stringify({
|
|
900
|
+
project: input.project,
|
|
901
|
+
userId: input.userId || "",
|
|
902
|
+
sessionId: input.sessionId || "",
|
|
903
|
+
payload: input.payload
|
|
904
|
+
});
|
|
905
|
+
return `evt_${stableHash(source)}`;
|
|
906
|
+
}
|
|
907
|
+
bindProcessHooks() {
|
|
908
|
+
if (typeof process === "undefined") return;
|
|
909
|
+
const proc = process;
|
|
910
|
+
const flushOnExit = () => {
|
|
911
|
+
void this.flush();
|
|
912
|
+
};
|
|
913
|
+
proc.once("beforeExit", flushOnExit);
|
|
914
|
+
proc.once("SIGINT", flushOnExit);
|
|
915
|
+
proc.once("SIGTERM", flushOnExit);
|
|
916
|
+
}
|
|
917
|
+
};
|
|
918
|
+
function createStorageQueueStore(key = "whisper_sdk_queue") {
|
|
919
|
+
const getStorage = () => {
|
|
920
|
+
const maybeStorage = globalThis.localStorage;
|
|
921
|
+
if (!maybeStorage || typeof maybeStorage !== "object") return null;
|
|
922
|
+
const candidate = maybeStorage;
|
|
923
|
+
if (typeof candidate.getItem !== "function" || typeof candidate.setItem !== "function") {
|
|
924
|
+
return null;
|
|
925
|
+
}
|
|
926
|
+
return {
|
|
927
|
+
getItem: candidate.getItem,
|
|
928
|
+
setItem: candidate.setItem
|
|
929
|
+
};
|
|
930
|
+
};
|
|
931
|
+
return {
|
|
932
|
+
async load() {
|
|
933
|
+
const storage = getStorage();
|
|
934
|
+
if (!storage) return [];
|
|
935
|
+
const raw = storage.getItem(key);
|
|
936
|
+
if (!raw) return [];
|
|
937
|
+
try {
|
|
938
|
+
const parsed = JSON.parse(raw);
|
|
939
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
940
|
+
} catch {
|
|
941
|
+
return [];
|
|
942
|
+
}
|
|
943
|
+
},
|
|
944
|
+
async save(items) {
|
|
945
|
+
const storage = getStorage();
|
|
946
|
+
if (!storage) return;
|
|
947
|
+
storage.setItem(key, JSON.stringify(items));
|
|
948
|
+
}
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
function createFileQueueStore(filePath) {
|
|
952
|
+
return {
|
|
953
|
+
async load() {
|
|
954
|
+
if (typeof process === "undefined") return [];
|
|
955
|
+
const fs = await import("fs/promises");
|
|
956
|
+
try {
|
|
957
|
+
const raw = await fs.readFile(filePath, "utf8");
|
|
958
|
+
const parsed = JSON.parse(raw);
|
|
959
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
960
|
+
} catch (error) {
|
|
961
|
+
const nodeError = error;
|
|
962
|
+
if (nodeError?.code === "ENOENT") {
|
|
963
|
+
return [];
|
|
964
|
+
}
|
|
965
|
+
return [];
|
|
966
|
+
}
|
|
967
|
+
},
|
|
968
|
+
async save(items) {
|
|
969
|
+
if (typeof process === "undefined") return;
|
|
970
|
+
const fs = await import("fs/promises");
|
|
971
|
+
const path = await import("path");
|
|
972
|
+
const dir = path.dirname(filePath);
|
|
973
|
+
await fs.mkdir(dir, { recursive: true });
|
|
974
|
+
await fs.writeFile(filePath, JSON.stringify(items), "utf8");
|
|
975
|
+
}
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
// ../src/sdk/modules/memory.ts
|
|
980
|
+
function isEndpointNotFound(error) {
|
|
981
|
+
return error instanceof RuntimeClientError && error.status === 404;
|
|
982
|
+
}
|
|
983
|
+
function toSotaType(memoryType) {
|
|
984
|
+
if (!memoryType) return void 0;
|
|
985
|
+
switch (memoryType) {
|
|
986
|
+
case "episodic":
|
|
987
|
+
return "event";
|
|
988
|
+
case "semantic":
|
|
989
|
+
return "factual";
|
|
990
|
+
case "procedural":
|
|
991
|
+
return "instruction";
|
|
992
|
+
default:
|
|
993
|
+
return memoryType;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
function toLegacyType(memoryType) {
|
|
997
|
+
if (!memoryType) return void 0;
|
|
998
|
+
switch (memoryType) {
|
|
999
|
+
case "event":
|
|
1000
|
+
return "episodic";
|
|
1001
|
+
case "instruction":
|
|
1002
|
+
return "procedural";
|
|
1003
|
+
case "preference":
|
|
1004
|
+
case "relationship":
|
|
1005
|
+
case "opinion":
|
|
1006
|
+
case "goal":
|
|
1007
|
+
return "semantic";
|
|
1008
|
+
default:
|
|
1009
|
+
return memoryType;
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
var MemoryModule = class {
|
|
1013
|
+
constructor(client, cache, queue, options = {}) {
|
|
1014
|
+
this.client = client;
|
|
1015
|
+
this.cache = cache;
|
|
1016
|
+
this.queue = queue;
|
|
1017
|
+
this.options = options;
|
|
1018
|
+
}
|
|
1019
|
+
resolveProject(project) {
|
|
1020
|
+
const value = project || this.options.defaultProject;
|
|
1021
|
+
if (!value) {
|
|
1022
|
+
throw new RuntimeClientError({
|
|
1023
|
+
code: "MISSING_PROJECT",
|
|
1024
|
+
message: "Project is required",
|
|
1025
|
+
retryable: false
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
return value;
|
|
1029
|
+
}
|
|
1030
|
+
invalidate(project, userId, sessionId) {
|
|
1031
|
+
if (this.options.cacheEnabled === false) {
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
const scope = this.cache.makeScopeKey(project, userId, sessionId);
|
|
1035
|
+
this.cache.invalidateScope(scope);
|
|
1036
|
+
}
|
|
1037
|
+
async add(params) {
|
|
1038
|
+
const project = this.resolveProject(params.project);
|
|
1039
|
+
const queueEnabled = this.options.queueEnabled !== false;
|
|
1040
|
+
const useQueue = queueEnabled && params.write_mode !== "sync" && params.async !== false;
|
|
1041
|
+
if (useQueue) {
|
|
1042
|
+
const queued = await this.queue.enqueue({
|
|
1043
|
+
project,
|
|
1044
|
+
userId: params.user_id,
|
|
1045
|
+
sessionId: params.session_id,
|
|
1046
|
+
payload: {
|
|
1047
|
+
content: params.content,
|
|
1048
|
+
memory_type: toSotaType(params.memory_type),
|
|
1049
|
+
user_id: params.user_id,
|
|
1050
|
+
session_id: params.session_id,
|
|
1051
|
+
agent_id: params.agent_id,
|
|
1052
|
+
importance: params.importance,
|
|
1053
|
+
confidence: params.confidence,
|
|
1054
|
+
metadata: params.metadata,
|
|
1055
|
+
document_date: params.document_date,
|
|
1056
|
+
event_date: params.event_date
|
|
1057
|
+
}
|
|
1058
|
+
});
|
|
1059
|
+
this.invalidate(project, params.user_id, params.session_id);
|
|
1060
|
+
return {
|
|
1061
|
+
success: true,
|
|
1062
|
+
mode: "async",
|
|
1063
|
+
queued: true,
|
|
1064
|
+
event_id: queued.eventId,
|
|
1065
|
+
accepted_at: queued.createdAt
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
try {
|
|
1069
|
+
const response = await this.client.request({
|
|
1070
|
+
endpoint: "/v1/memory",
|
|
1071
|
+
method: "POST",
|
|
1072
|
+
operation: "writeAck",
|
|
1073
|
+
body: {
|
|
1074
|
+
project,
|
|
1075
|
+
content: params.content,
|
|
1076
|
+
memory_type: toSotaType(params.memory_type),
|
|
1077
|
+
user_id: params.user_id,
|
|
1078
|
+
session_id: params.session_id,
|
|
1079
|
+
agent_id: params.agent_id,
|
|
1080
|
+
importance: params.importance,
|
|
1081
|
+
confidence: params.confidence,
|
|
1082
|
+
metadata: params.metadata,
|
|
1083
|
+
document_date: params.document_date,
|
|
1084
|
+
event_date: params.event_date,
|
|
1085
|
+
write_mode: "sync"
|
|
1086
|
+
}
|
|
1087
|
+
});
|
|
1088
|
+
this.invalidate(project, params.user_id, params.session_id);
|
|
1089
|
+
return {
|
|
1090
|
+
success: true,
|
|
1091
|
+
mode: "sync",
|
|
1092
|
+
trace_id: response.traceId
|
|
1093
|
+
};
|
|
1094
|
+
} catch (error) {
|
|
1095
|
+
if (this.client.getCompatMode() !== "fallback" || !isEndpointNotFound(error)) {
|
|
1096
|
+
throw error;
|
|
1097
|
+
}
|
|
1098
|
+
await this.client.request({
|
|
1099
|
+
endpoint: "/v1/memories",
|
|
1100
|
+
method: "POST",
|
|
1101
|
+
operation: "writeAck",
|
|
1102
|
+
body: {
|
|
1103
|
+
project,
|
|
1104
|
+
content: params.content,
|
|
1105
|
+
memory_type: toLegacyType(params.memory_type),
|
|
1106
|
+
user_id: params.user_id,
|
|
1107
|
+
session_id: params.session_id,
|
|
1108
|
+
agent_id: params.agent_id,
|
|
1109
|
+
importance: params.importance,
|
|
1110
|
+
metadata: params.metadata
|
|
1111
|
+
}
|
|
1112
|
+
});
|
|
1113
|
+
this.invalidate(project, params.user_id, params.session_id);
|
|
1114
|
+
return {
|
|
1115
|
+
success: true,
|
|
1116
|
+
mode: "sync"
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
async addBulk(params) {
|
|
1121
|
+
const project = this.resolveProject(params.project);
|
|
1122
|
+
if (!Array.isArray(params.memories) || params.memories.length === 0) {
|
|
1123
|
+
throw new RuntimeClientError({
|
|
1124
|
+
code: "VALIDATION_ERROR",
|
|
1125
|
+
message: "memories is required",
|
|
1126
|
+
retryable: false
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
const queueEnabled = this.options.queueEnabled !== false;
|
|
1130
|
+
const useQueue = queueEnabled && params.write_mode !== "sync" && params.async !== false;
|
|
1131
|
+
if (useQueue) {
|
|
1132
|
+
const queued = await Promise.all(
|
|
1133
|
+
params.memories.map(
|
|
1134
|
+
(memory) => this.queue.enqueue({
|
|
1135
|
+
project,
|
|
1136
|
+
userId: memory.user_id,
|
|
1137
|
+
sessionId: memory.session_id,
|
|
1138
|
+
payload: {
|
|
1139
|
+
content: memory.content,
|
|
1140
|
+
memory_type: toSotaType(memory.memory_type),
|
|
1141
|
+
user_id: memory.user_id,
|
|
1142
|
+
session_id: memory.session_id,
|
|
1143
|
+
agent_id: memory.agent_id,
|
|
1144
|
+
importance: memory.importance,
|
|
1145
|
+
confidence: memory.confidence,
|
|
1146
|
+
metadata: memory.metadata,
|
|
1147
|
+
document_date: memory.document_date,
|
|
1148
|
+
event_date: memory.event_date
|
|
1149
|
+
}
|
|
1150
|
+
})
|
|
1151
|
+
)
|
|
1152
|
+
);
|
|
1153
|
+
for (const memory of params.memories) {
|
|
1154
|
+
this.invalidate(project, memory.user_id, memory.session_id);
|
|
1155
|
+
}
|
|
1156
|
+
return {
|
|
1157
|
+
success: true,
|
|
1158
|
+
mode: "async",
|
|
1159
|
+
queued: true,
|
|
1160
|
+
created: queued.length
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1163
|
+
try {
|
|
1164
|
+
const response = await this.client.request({
|
|
1165
|
+
endpoint: "/v1/memory/bulk",
|
|
1166
|
+
method: "POST",
|
|
1167
|
+
operation: "bulk",
|
|
1168
|
+
body: {
|
|
1169
|
+
project,
|
|
1170
|
+
memories: params.memories.map((memory) => ({
|
|
1171
|
+
...memory,
|
|
1172
|
+
memory_type: toSotaType(memory.memory_type)
|
|
1173
|
+
})),
|
|
1174
|
+
write_mode: "sync"
|
|
1175
|
+
}
|
|
1176
|
+
});
|
|
1177
|
+
for (const memory of params.memories) {
|
|
1178
|
+
this.invalidate(project, memory.user_id, memory.session_id);
|
|
1179
|
+
}
|
|
1180
|
+
return {
|
|
1181
|
+
success: true,
|
|
1182
|
+
mode: "sync",
|
|
1183
|
+
trace_id: response.traceId
|
|
1184
|
+
};
|
|
1185
|
+
} catch (error) {
|
|
1186
|
+
if (this.client.getCompatMode() !== "fallback" || !isEndpointNotFound(error)) {
|
|
1187
|
+
throw error;
|
|
1188
|
+
}
|
|
1189
|
+
await Promise.all(
|
|
1190
|
+
params.memories.map(
|
|
1191
|
+
(memory) => this.add({
|
|
1192
|
+
project,
|
|
1193
|
+
...memory,
|
|
1194
|
+
write_mode: "sync"
|
|
1195
|
+
})
|
|
1196
|
+
)
|
|
1197
|
+
);
|
|
1198
|
+
return {
|
|
1199
|
+
success: true,
|
|
1200
|
+
mode: "sync"
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
async search(params) {
|
|
1205
|
+
const project = this.resolveProject(params.project);
|
|
1206
|
+
const topK = params.top_k || 10;
|
|
1207
|
+
const profile = params.profile || "fast";
|
|
1208
|
+
const includePending = params.include_pending !== false;
|
|
1209
|
+
const cacheKey = this.cache.makeKey({
|
|
1210
|
+
project,
|
|
1211
|
+
userId: params.user_id,
|
|
1212
|
+
sessionId: params.session_id,
|
|
1213
|
+
query: params.query,
|
|
1214
|
+
topK,
|
|
1215
|
+
profile,
|
|
1216
|
+
includePending
|
|
1217
|
+
});
|
|
1218
|
+
if (this.options.cacheEnabled !== false) {
|
|
1219
|
+
const cached = this.cache.get(cacheKey);
|
|
1220
|
+
if (cached) {
|
|
1221
|
+
return {
|
|
1222
|
+
...cached,
|
|
1223
|
+
cache_hit: true
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
try {
|
|
1228
|
+
const response = await this.client.request({
|
|
1229
|
+
endpoint: "/v1/memory/search",
|
|
1230
|
+
method: "POST",
|
|
1231
|
+
operation: "search",
|
|
1232
|
+
idempotent: true,
|
|
1233
|
+
body: {
|
|
1234
|
+
project,
|
|
1235
|
+
query: params.query,
|
|
1236
|
+
user_id: params.user_id,
|
|
1237
|
+
session_id: params.session_id,
|
|
1238
|
+
top_k: topK,
|
|
1239
|
+
profile,
|
|
1240
|
+
include_pending: includePending,
|
|
1241
|
+
memory_types: params.memory_type ? [toSotaType(params.memory_type)] : void 0
|
|
1242
|
+
}
|
|
1243
|
+
});
|
|
1244
|
+
const data = {
|
|
1245
|
+
...response.data || {},
|
|
1246
|
+
cache_hit: false
|
|
1247
|
+
};
|
|
1248
|
+
if (this.options.cacheEnabled !== false) {
|
|
1249
|
+
const scope = this.cache.makeScopeKey(project, params.user_id, params.session_id);
|
|
1250
|
+
this.cache.set(cacheKey, scope, data);
|
|
1251
|
+
}
|
|
1252
|
+
return data;
|
|
1253
|
+
} catch (error) {
|
|
1254
|
+
if (this.client.getCompatMode() !== "fallback" || !isEndpointNotFound(error)) {
|
|
1255
|
+
throw error;
|
|
1256
|
+
}
|
|
1257
|
+
const legacy = await this.client.request({
|
|
1258
|
+
endpoint: "/v1/memories/search",
|
|
1259
|
+
method: "POST",
|
|
1260
|
+
operation: "search",
|
|
1261
|
+
idempotent: true,
|
|
1262
|
+
body: {
|
|
1263
|
+
project,
|
|
1264
|
+
query: params.query,
|
|
1265
|
+
user_id: params.user_id,
|
|
1266
|
+
session_id: params.session_id,
|
|
1267
|
+
top_k: topK,
|
|
1268
|
+
memory_type: toLegacyType(params.memory_type)
|
|
1269
|
+
}
|
|
1270
|
+
});
|
|
1271
|
+
const data = {
|
|
1272
|
+
...legacy.data || {},
|
|
1273
|
+
cache_hit: false
|
|
1274
|
+
};
|
|
1275
|
+
if (this.options.cacheEnabled !== false) {
|
|
1276
|
+
const scope = this.cache.makeScopeKey(project, params.user_id, params.session_id);
|
|
1277
|
+
this.cache.set(cacheKey, scope, data);
|
|
1278
|
+
}
|
|
1279
|
+
return data;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
async getUserProfile(params) {
|
|
1283
|
+
const project = this.resolveProject(params.project);
|
|
1284
|
+
const query = new URLSearchParams({
|
|
1285
|
+
project,
|
|
1286
|
+
...params.include_pending !== void 0 ? { include_pending: String(params.include_pending) } : {},
|
|
1287
|
+
...params.memory_types ? { memory_types: params.memory_types } : {}
|
|
1288
|
+
});
|
|
1289
|
+
try {
|
|
1290
|
+
const response = await this.client.request({
|
|
1291
|
+
endpoint: `/v1/memory/profile/${params.user_id}?${query}`,
|
|
1292
|
+
method: "GET",
|
|
1293
|
+
operation: "profile",
|
|
1294
|
+
idempotent: true
|
|
1295
|
+
});
|
|
1296
|
+
return response.data;
|
|
1297
|
+
} catch (error) {
|
|
1298
|
+
if (this.client.getCompatMode() !== "fallback" || !isEndpointNotFound(error)) {
|
|
1299
|
+
throw error;
|
|
1300
|
+
}
|
|
1301
|
+
const legacyQuery = new URLSearchParams({
|
|
1302
|
+
project,
|
|
1303
|
+
user_id: params.user_id,
|
|
1304
|
+
limit: "200"
|
|
1305
|
+
});
|
|
1306
|
+
const legacy = await this.client.request({
|
|
1307
|
+
endpoint: `/v1/memories?${legacyQuery}`,
|
|
1308
|
+
method: "GET",
|
|
1309
|
+
operation: "profile",
|
|
1310
|
+
idempotent: true
|
|
1311
|
+
});
|
|
1312
|
+
const memories = Array.isArray(legacy.data?.memories) ? legacy.data.memories : [];
|
|
1313
|
+
return {
|
|
1314
|
+
user_id: params.user_id,
|
|
1315
|
+
memories,
|
|
1316
|
+
count: memories.length
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
async getSessionMemories(params) {
|
|
1321
|
+
const project = this.resolveProject(params.project);
|
|
1322
|
+
const query = new URLSearchParams({
|
|
1323
|
+
project,
|
|
1324
|
+
...params.limit ? { limit: String(params.limit) } : {},
|
|
1325
|
+
...params.include_pending !== void 0 ? { include_pending: String(params.include_pending) } : {}
|
|
1326
|
+
});
|
|
1327
|
+
const response = await this.client.request({
|
|
1328
|
+
endpoint: `/v1/memory/session/${params.session_id}?${query}`,
|
|
1329
|
+
method: "GET",
|
|
1330
|
+
operation: "profile",
|
|
1331
|
+
idempotent: true
|
|
1332
|
+
});
|
|
1333
|
+
return response.data;
|
|
1334
|
+
}
|
|
1335
|
+
async get(memoryId) {
|
|
1336
|
+
try {
|
|
1337
|
+
const response = await this.client.request({
|
|
1338
|
+
endpoint: `/v1/memory/${memoryId}`,
|
|
1339
|
+
method: "GET",
|
|
1340
|
+
operation: "get",
|
|
1341
|
+
idempotent: true
|
|
1342
|
+
});
|
|
1343
|
+
return response.data;
|
|
1344
|
+
} catch (error) {
|
|
1345
|
+
if (this.client.getCompatMode() !== "fallback" || !isEndpointNotFound(error)) {
|
|
1346
|
+
throw error;
|
|
1347
|
+
}
|
|
1348
|
+
const legacy = await this.client.request({
|
|
1349
|
+
endpoint: `/v1/memories/${memoryId}`,
|
|
1350
|
+
method: "GET",
|
|
1351
|
+
operation: "get",
|
|
1352
|
+
idempotent: true
|
|
1353
|
+
});
|
|
1354
|
+
return legacy.data;
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
async update(memoryId, params) {
|
|
1358
|
+
try {
|
|
1359
|
+
await this.client.request({
|
|
1360
|
+
endpoint: `/v1/memory/${memoryId}`,
|
|
1361
|
+
method: "PUT",
|
|
1362
|
+
operation: "writeAck",
|
|
1363
|
+
body: params
|
|
1364
|
+
});
|
|
1365
|
+
return { success: true };
|
|
1366
|
+
} catch (error) {
|
|
1367
|
+
if (this.client.getCompatMode() !== "fallback" || !isEndpointNotFound(error)) {
|
|
1368
|
+
throw error;
|
|
1369
|
+
}
|
|
1370
|
+
await this.client.request({
|
|
1371
|
+
endpoint: `/v1/memories/${memoryId}`,
|
|
1372
|
+
method: "PUT",
|
|
1373
|
+
operation: "writeAck",
|
|
1374
|
+
body: { content: params.content }
|
|
1375
|
+
});
|
|
1376
|
+
return { success: true };
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
async delete(memoryId) {
|
|
1380
|
+
try {
|
|
1381
|
+
await this.client.request({
|
|
1382
|
+
endpoint: `/v1/memory/${memoryId}`,
|
|
1383
|
+
method: "DELETE",
|
|
1384
|
+
operation: "writeAck"
|
|
1385
|
+
});
|
|
1386
|
+
return { success: true, deleted: memoryId };
|
|
1387
|
+
} catch (error) {
|
|
1388
|
+
if (this.client.getCompatMode() !== "fallback" || !isEndpointNotFound(error)) {
|
|
1389
|
+
throw error;
|
|
1390
|
+
}
|
|
1391
|
+
await this.client.request({
|
|
1392
|
+
endpoint: `/v1/memories/${memoryId}`,
|
|
1393
|
+
method: "DELETE",
|
|
1394
|
+
operation: "writeAck"
|
|
1395
|
+
});
|
|
1396
|
+
return { success: true, deleted: memoryId };
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
async flag(params) {
|
|
1400
|
+
try {
|
|
1401
|
+
await this.client.request({
|
|
1402
|
+
endpoint: `/v1/memory/${params.memoryId}/flag`,
|
|
1403
|
+
method: "POST",
|
|
1404
|
+
operation: "writeAck",
|
|
1405
|
+
body: {
|
|
1406
|
+
reason: params.reason,
|
|
1407
|
+
severity: params.severity || "medium"
|
|
1408
|
+
}
|
|
1409
|
+
});
|
|
1410
|
+
return { success: true };
|
|
1411
|
+
} catch (error) {
|
|
1412
|
+
if (this.client.getCompatMode() !== "fallback" || !isEndpointNotFound(error)) {
|
|
1413
|
+
throw error;
|
|
1414
|
+
}
|
|
1415
|
+
await this.client.request({
|
|
1416
|
+
endpoint: `/v1/memory/${params.memoryId}`,
|
|
1417
|
+
method: "PUT",
|
|
1418
|
+
operation: "writeAck",
|
|
1419
|
+
body: {
|
|
1420
|
+
content: `[FLAGGED:${params.severity || "medium"}] ${params.reason}`
|
|
1421
|
+
}
|
|
1422
|
+
});
|
|
1423
|
+
return { success: true };
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
};
|
|
1427
|
+
|
|
1428
|
+
// ../src/sdk/modules/session.ts
|
|
1429
|
+
function randomSessionId() {
|
|
1430
|
+
return `sess_${stableHash(`${Date.now()}_${Math.random()}`)}`;
|
|
1431
|
+
}
|
|
1432
|
+
function assertTransition(current, next) {
|
|
1433
|
+
const allowed = {
|
|
1434
|
+
created: ["active", "ended", "archived"],
|
|
1435
|
+
active: ["suspended", "ended", "archived"],
|
|
1436
|
+
suspended: ["resumed", "ended", "archived"],
|
|
1437
|
+
resumed: ["suspended", "ended", "archived"],
|
|
1438
|
+
ended: ["archived"],
|
|
1439
|
+
archived: []
|
|
1440
|
+
};
|
|
1441
|
+
if (!allowed[current].includes(next)) {
|
|
1442
|
+
throw new RuntimeClientError({
|
|
1443
|
+
code: "INVALID_SESSION_STATE",
|
|
1444
|
+
message: `Invalid session transition ${current} -> ${next}`,
|
|
1445
|
+
retryable: false
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
var SessionModule = class {
|
|
1450
|
+
constructor(memory, defaultProject) {
|
|
1451
|
+
this.memory = memory;
|
|
1452
|
+
this.defaultProject = defaultProject;
|
|
1453
|
+
}
|
|
1454
|
+
sessions = /* @__PURE__ */ new Map();
|
|
1455
|
+
resolveProject(project) {
|
|
1456
|
+
const value = project || this.defaultProject;
|
|
1457
|
+
if (!value) {
|
|
1458
|
+
throw new RuntimeClientError({
|
|
1459
|
+
code: "MISSING_PROJECT",
|
|
1460
|
+
message: "Project is required",
|
|
1461
|
+
retryable: false
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
return value;
|
|
1465
|
+
}
|
|
1466
|
+
ensure(sessionId) {
|
|
1467
|
+
const found = this.sessions.get(sessionId);
|
|
1468
|
+
if (!found) {
|
|
1469
|
+
throw new RuntimeClientError({
|
|
1470
|
+
code: "SESSION_NOT_FOUND",
|
|
1471
|
+
message: `Unknown session ${sessionId}`,
|
|
1472
|
+
retryable: false
|
|
1473
|
+
});
|
|
1474
|
+
}
|
|
1475
|
+
return found;
|
|
1476
|
+
}
|
|
1477
|
+
async start(params) {
|
|
1478
|
+
const project = this.resolveProject(params.project);
|
|
1479
|
+
const sessionId = params.sessionId || randomSessionId();
|
|
1480
|
+
const now = nowIso();
|
|
1481
|
+
const record = {
|
|
1482
|
+
sessionId,
|
|
1483
|
+
project,
|
|
234
1484
|
userId: params.userId,
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
1485
|
+
state: "active",
|
|
1486
|
+
sequence: 0,
|
|
1487
|
+
metadata: params.metadata,
|
|
1488
|
+
createdAt: now,
|
|
1489
|
+
updatedAt: now
|
|
1490
|
+
};
|
|
1491
|
+
this.sessions.set(sessionId, record);
|
|
1492
|
+
return {
|
|
1493
|
+
sessionId,
|
|
1494
|
+
state: record.state,
|
|
1495
|
+
createdAt: now
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
async event(params) {
|
|
1499
|
+
const session = this.ensure(params.sessionId);
|
|
1500
|
+
if (session.state !== "active" && session.state !== "resumed") {
|
|
1501
|
+
throw new RuntimeClientError({
|
|
1502
|
+
code: "INVALID_SESSION_STATE",
|
|
1503
|
+
message: `Cannot append event in ${session.state} state`,
|
|
1504
|
+
retryable: false
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
session.sequence += 1;
|
|
1508
|
+
session.updatedAt = nowIso();
|
|
1509
|
+
const eventId = `evt_${stableHash(JSON.stringify({
|
|
1510
|
+
sessionId: session.sessionId,
|
|
1511
|
+
seq: session.sequence,
|
|
1512
|
+
type: params.type,
|
|
1513
|
+
content: params.content,
|
|
1514
|
+
parent: params.parentEventId || ""
|
|
1515
|
+
}))}`;
|
|
1516
|
+
await this.memory.add({
|
|
1517
|
+
project: session.project,
|
|
1518
|
+
content: `${params.type}: ${params.content}`,
|
|
1519
|
+
memory_type: "event",
|
|
1520
|
+
user_id: session.userId,
|
|
1521
|
+
session_id: session.sessionId,
|
|
1522
|
+
metadata: {
|
|
1523
|
+
session_event: true,
|
|
1524
|
+
event_id: eventId,
|
|
1525
|
+
sequence: session.sequence,
|
|
1526
|
+
parent_event_id: params.parentEventId,
|
|
1527
|
+
...session.metadata,
|
|
1528
|
+
...params.metadata || {}
|
|
1529
|
+
},
|
|
1530
|
+
write_mode: "async"
|
|
238
1531
|
});
|
|
239
|
-
const prompt = contextResult.context ? `${contextResult.context}
|
|
240
|
-
|
|
241
|
-
User: ${params.userMessage}` : params.userMessage;
|
|
242
|
-
const response = await params.generate(prompt);
|
|
243
|
-
const captureResult = await this.captureSession(
|
|
244
|
-
[
|
|
245
|
-
{ role: "user", content: params.userMessage },
|
|
246
|
-
{ role: "assistant", content: response }
|
|
247
|
-
],
|
|
248
|
-
{
|
|
249
|
-
userId: params.userId,
|
|
250
|
-
sessionId: params.sessionId,
|
|
251
|
-
project: params.project
|
|
252
|
-
}
|
|
253
|
-
);
|
|
254
1532
|
return {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
extracted: captureResult.extracted
|
|
1533
|
+
success: true,
|
|
1534
|
+
eventId,
|
|
1535
|
+
sequence: session.sequence
|
|
259
1536
|
};
|
|
260
1537
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
1538
|
+
async suspend(params) {
|
|
1539
|
+
const session = this.ensure(params.sessionId);
|
|
1540
|
+
assertTransition(session.state, "suspended");
|
|
1541
|
+
session.state = "suspended";
|
|
1542
|
+
session.updatedAt = nowIso();
|
|
1543
|
+
return { sessionId: session.sessionId, state: session.state };
|
|
1544
|
+
}
|
|
1545
|
+
async resume(params) {
|
|
1546
|
+
const session = this.ensure(params.sessionId);
|
|
1547
|
+
const target = session.state === "suspended" ? "resumed" : "active";
|
|
1548
|
+
assertTransition(session.state, target);
|
|
1549
|
+
session.state = target;
|
|
1550
|
+
session.updatedAt = nowIso();
|
|
1551
|
+
return { sessionId: session.sessionId, state: session.state };
|
|
1552
|
+
}
|
|
1553
|
+
async end(params) {
|
|
1554
|
+
const session = this.ensure(params.sessionId);
|
|
1555
|
+
assertTransition(session.state, "ended");
|
|
1556
|
+
session.state = "ended";
|
|
1557
|
+
session.updatedAt = nowIso();
|
|
1558
|
+
return { sessionId: session.sessionId, state: session.state };
|
|
1559
|
+
}
|
|
1560
|
+
async archive(params) {
|
|
1561
|
+
const session = this.ensure(params.sessionId);
|
|
1562
|
+
assertTransition(session.state, "archived");
|
|
1563
|
+
session.state = "archived";
|
|
1564
|
+
session.updatedAt = nowIso();
|
|
1565
|
+
return { sessionId: session.sessionId, state: session.state };
|
|
266
1566
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
}
|
|
274
|
-
if (bulkResponse?.memory?.id) {
|
|
275
|
-
ids.push(bulkResponse.memory.id);
|
|
276
|
-
}
|
|
277
|
-
if (bulkResponse?.id) {
|
|
278
|
-
ids.push(bulkResponse.id);
|
|
279
|
-
}
|
|
280
|
-
return Array.from(new Set(ids));
|
|
1567
|
+
};
|
|
1568
|
+
|
|
1569
|
+
// ../src/sdk/modules/profile.ts
|
|
1570
|
+
var ProfileModule = class {
|
|
1571
|
+
constructor(memory) {
|
|
1572
|
+
this.memory = memory;
|
|
281
1573
|
}
|
|
282
|
-
async
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
1574
|
+
async getUserProfile(params) {
|
|
1575
|
+
return this.memory.getUserProfile(params);
|
|
1576
|
+
}
|
|
1577
|
+
async getSessionMemories(params) {
|
|
1578
|
+
return this.memory.getSessionMemories(params);
|
|
1579
|
+
}
|
|
1580
|
+
};
|
|
1581
|
+
|
|
1582
|
+
// ../src/sdk/modules/analytics.ts
|
|
1583
|
+
var AnalyticsModule = class {
|
|
1584
|
+
constructor(diagnostics, queue) {
|
|
1585
|
+
this.diagnostics = diagnostics;
|
|
1586
|
+
this.queue = queue;
|
|
1587
|
+
}
|
|
1588
|
+
diagnosticsSnapshot() {
|
|
1589
|
+
return this.diagnostics.snapshot();
|
|
1590
|
+
}
|
|
1591
|
+
queueStatus() {
|
|
1592
|
+
return this.queue.status();
|
|
1593
|
+
}
|
|
1594
|
+
};
|
|
1595
|
+
|
|
1596
|
+
// ../src/sdk/whisper.ts
|
|
1597
|
+
var WhisperClient = class _WhisperClient {
|
|
1598
|
+
constructor(config) {
|
|
1599
|
+
this.config = config;
|
|
1600
|
+
this.diagnosticsStore = new DiagnosticsStore(config.telemetry?.maxEntries || 1e3);
|
|
1601
|
+
this.runtimeClient = new RuntimeClient(
|
|
1602
|
+
{
|
|
1603
|
+
apiKey: config.apiKey,
|
|
1604
|
+
baseUrl: config.baseUrl,
|
|
1605
|
+
compatMode: config.compatMode || "fallback",
|
|
1606
|
+
timeouts: config.timeouts,
|
|
1607
|
+
retryPolicy: config.retryPolicy
|
|
1608
|
+
},
|
|
1609
|
+
this.diagnosticsStore
|
|
1610
|
+
);
|
|
1611
|
+
this.searchCache = new SearchResponseCache(
|
|
1612
|
+
config.cache?.ttlMs ?? 7e3,
|
|
1613
|
+
config.cache?.capacity ?? 500
|
|
1614
|
+
);
|
|
1615
|
+
const queueStore = config.queue?.persistence === "storage" ? createStorageQueueStore() : config.queue?.persistence === "file" && config.queue.filePath ? createFileQueueStore(config.queue.filePath) : new InMemoryQueueStore();
|
|
1616
|
+
this.writeQueue = new WriteQueue({
|
|
1617
|
+
store: queueStore,
|
|
1618
|
+
maxBatchSize: config.queue?.maxBatchSize ?? 50,
|
|
1619
|
+
flushIntervalMs: config.queue?.flushIntervalMs ?? 100,
|
|
1620
|
+
maxAttempts: config.queue?.maxAttempts ?? 2,
|
|
1621
|
+
flushHandler: async (items) => {
|
|
1622
|
+
if (items.length === 0) return;
|
|
1623
|
+
const project = items[0].project;
|
|
1624
|
+
const memories = items.map((item) => ({
|
|
1625
|
+
...item.payload,
|
|
1626
|
+
user_id: item.payload.user_id ?? item.userId,
|
|
1627
|
+
session_id: item.payload.session_id ?? item.sessionId,
|
|
1628
|
+
metadata: {
|
|
1629
|
+
...item.payload.metadata || {},
|
|
1630
|
+
event_id: item.eventId,
|
|
1631
|
+
queued_at: item.createdAt
|
|
1632
|
+
}
|
|
1633
|
+
}));
|
|
1634
|
+
try {
|
|
1635
|
+
await this.runtimeClient.request({
|
|
1636
|
+
endpoint: "/v1/memory/bulk",
|
|
1637
|
+
method: "POST",
|
|
1638
|
+
operation: "bulk",
|
|
1639
|
+
body: {
|
|
1640
|
+
project,
|
|
1641
|
+
write_mode: "async",
|
|
1642
|
+
memories
|
|
1643
|
+
}
|
|
1644
|
+
});
|
|
1645
|
+
} catch (error) {
|
|
1646
|
+
if (this.runtimeClient.getCompatMode() !== "fallback" || !(error instanceof RuntimeClientError) || error.status !== 404) {
|
|
1647
|
+
throw error;
|
|
1648
|
+
}
|
|
1649
|
+
await Promise.all(
|
|
1650
|
+
memories.map(async (memory) => {
|
|
1651
|
+
try {
|
|
1652
|
+
await this.runtimeClient.request({
|
|
1653
|
+
endpoint: "/v1/memory",
|
|
1654
|
+
method: "POST",
|
|
1655
|
+
operation: "writeAck",
|
|
1656
|
+
body: {
|
|
1657
|
+
project,
|
|
1658
|
+
...memory,
|
|
1659
|
+
write_mode: "sync"
|
|
1660
|
+
}
|
|
1661
|
+
});
|
|
1662
|
+
} catch (fallbackError) {
|
|
1663
|
+
if (this.runtimeClient.getCompatMode() !== "fallback" || !(fallbackError instanceof RuntimeClientError) || fallbackError.status !== 404) {
|
|
1664
|
+
throw fallbackError;
|
|
1665
|
+
}
|
|
1666
|
+
await this.runtimeClient.request({
|
|
1667
|
+
endpoint: "/v1/memories",
|
|
1668
|
+
method: "POST",
|
|
1669
|
+
operation: "writeAck",
|
|
1670
|
+
body: {
|
|
1671
|
+
project,
|
|
1672
|
+
...memory,
|
|
1673
|
+
memory_type: memory.memory_type === "event" ? "episodic" : memory.memory_type
|
|
1674
|
+
}
|
|
1675
|
+
});
|
|
1676
|
+
}
|
|
1677
|
+
})
|
|
1678
|
+
);
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
});
|
|
1682
|
+
if (config.queue?.enabled !== false) {
|
|
1683
|
+
void this.writeQueue.start();
|
|
286
1684
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
session_id: options?.sessionId ?? this.sessionId,
|
|
296
|
-
allow_legacy_fallback: true
|
|
297
|
-
});
|
|
298
|
-
extracted += 1;
|
|
299
|
-
} catch {
|
|
1685
|
+
this.memoryModule = new MemoryModule(
|
|
1686
|
+
this.runtimeClient,
|
|
1687
|
+
this.searchCache,
|
|
1688
|
+
this.writeQueue,
|
|
1689
|
+
{
|
|
1690
|
+
defaultProject: config.project,
|
|
1691
|
+
cacheEnabled: config.cache?.enabled !== false,
|
|
1692
|
+
queueEnabled: config.queue?.enabled !== false
|
|
300
1693
|
}
|
|
1694
|
+
);
|
|
1695
|
+
this.sessionModule = new SessionModule(this.memoryModule, config.project);
|
|
1696
|
+
this.profileModule = new ProfileModule(this.memoryModule);
|
|
1697
|
+
this.analyticsModule = new AnalyticsModule(this.diagnosticsStore, this.writeQueue);
|
|
1698
|
+
this.diagnostics = {
|
|
1699
|
+
getLast: (limit) => this.diagnosticsStore.getLast(limit),
|
|
1700
|
+
subscribe: (fn) => this.diagnosticsStore.subscribe(fn),
|
|
1701
|
+
snapshot: () => this.diagnosticsStore.snapshot()
|
|
1702
|
+
};
|
|
1703
|
+
this.queue = {
|
|
1704
|
+
flush: () => this.writeQueue.flush(),
|
|
1705
|
+
status: () => this.writeQueue.status()
|
|
1706
|
+
};
|
|
1707
|
+
this.memory = {
|
|
1708
|
+
add: (params) => this.memoryModule.add(params),
|
|
1709
|
+
addBulk: (params) => this.memoryModule.addBulk(params),
|
|
1710
|
+
search: (params) => this.memoryModule.search(params),
|
|
1711
|
+
get: (memoryId) => this.memoryModule.get(memoryId),
|
|
1712
|
+
getUserProfile: (params) => this.memoryModule.getUserProfile(params),
|
|
1713
|
+
getSessionMemories: (params) => this.memoryModule.getSessionMemories(params),
|
|
1714
|
+
update: (memoryId, params) => this.memoryModule.update(memoryId, params),
|
|
1715
|
+
delete: (memoryId) => this.memoryModule.delete(memoryId),
|
|
1716
|
+
flag: (params) => this.memoryModule.flag(params)
|
|
1717
|
+
};
|
|
1718
|
+
this.session = {
|
|
1719
|
+
start: (params) => this.sessionModule.start(params),
|
|
1720
|
+
event: (params) => this.sessionModule.event(params),
|
|
1721
|
+
suspend: (params) => this.sessionModule.suspend(params),
|
|
1722
|
+
resume: (params) => this.sessionModule.resume(params),
|
|
1723
|
+
end: (params) => this.sessionModule.end(params)
|
|
1724
|
+
};
|
|
1725
|
+
this.profile = {
|
|
1726
|
+
getUserProfile: (params) => this.profileModule.getUserProfile(params),
|
|
1727
|
+
getSessionMemories: (params) => this.profileModule.getSessionMemories(params)
|
|
1728
|
+
};
|
|
1729
|
+
this.analytics = {
|
|
1730
|
+
diagnosticsSnapshot: () => this.analyticsModule.diagnosticsSnapshot(),
|
|
1731
|
+
queueStatus: () => this.analyticsModule.queueStatus()
|
|
1732
|
+
};
|
|
1733
|
+
}
|
|
1734
|
+
diagnostics;
|
|
1735
|
+
queue;
|
|
1736
|
+
memory;
|
|
1737
|
+
session;
|
|
1738
|
+
profile;
|
|
1739
|
+
analytics;
|
|
1740
|
+
runtimeClient;
|
|
1741
|
+
diagnosticsStore;
|
|
1742
|
+
searchCache;
|
|
1743
|
+
writeQueue;
|
|
1744
|
+
memoryModule;
|
|
1745
|
+
sessionModule;
|
|
1746
|
+
profileModule;
|
|
1747
|
+
analyticsModule;
|
|
1748
|
+
static fromEnv(overrides = {}) {
|
|
1749
|
+
const env = typeof process !== "undefined" ? process.env : {};
|
|
1750
|
+
const apiKey = overrides.apiKey || env.WHISPER_API_KEY || env.USEWHISPER_API_KEY || env.API_KEY;
|
|
1751
|
+
if (!apiKey) {
|
|
1752
|
+
throw new Error("Missing API key. Set WHISPER_API_KEY / USEWHISPER_API_KEY / API_KEY.");
|
|
301
1753
|
}
|
|
302
|
-
return {
|
|
1754
|
+
return new _WhisperClient({
|
|
1755
|
+
apiKey,
|
|
1756
|
+
baseUrl: overrides.baseUrl || env.WHISPER_BASE_URL || env.API_BASE_URL || "https://context.usewhisper.dev",
|
|
1757
|
+
project: overrides.project || env.WHISPER_PROJECT || env.PROJECT,
|
|
1758
|
+
...overrides
|
|
1759
|
+
});
|
|
1760
|
+
}
|
|
1761
|
+
withRunContext(context) {
|
|
1762
|
+
const base = this;
|
|
1763
|
+
return {
|
|
1764
|
+
memory: {
|
|
1765
|
+
add: (params) => base.memory.add({
|
|
1766
|
+
...params,
|
|
1767
|
+
project: params.project || context.project || base.config.project,
|
|
1768
|
+
user_id: params.user_id || context.userId,
|
|
1769
|
+
session_id: params.session_id || context.sessionId
|
|
1770
|
+
}),
|
|
1771
|
+
search: (params) => base.memory.search({
|
|
1772
|
+
...params,
|
|
1773
|
+
project: params.project || context.project || base.config.project,
|
|
1774
|
+
user_id: params.user_id || context.userId,
|
|
1775
|
+
session_id: params.session_id || context.sessionId
|
|
1776
|
+
})
|
|
1777
|
+
},
|
|
1778
|
+
session: {
|
|
1779
|
+
event: (params) => base.session.event({
|
|
1780
|
+
...params,
|
|
1781
|
+
sessionId: params.sessionId || context.sessionId || ""
|
|
1782
|
+
})
|
|
1783
|
+
},
|
|
1784
|
+
queue: base.queue,
|
|
1785
|
+
diagnostics: base.diagnostics
|
|
1786
|
+
};
|
|
1787
|
+
}
|
|
1788
|
+
async shutdown() {
|
|
1789
|
+
await this.writeQueue.stop();
|
|
303
1790
|
}
|
|
304
1791
|
};
|
|
305
|
-
var
|
|
1792
|
+
var whisper_default = WhisperClient;
|
|
306
1793
|
|
|
307
1794
|
// ../src/sdk/middleware.ts
|
|
308
1795
|
var WhisperAgentMiddleware = class {
|
|
@@ -373,14 +1860,409 @@ function createAgentMiddleware(config) {
|
|
|
373
1860
|
return new WhisperAgentMiddleware(config);
|
|
374
1861
|
}
|
|
375
1862
|
|
|
1863
|
+
// ../src/sdk/adapters/langchain.ts
|
|
1864
|
+
var LangChainMemoryAdapter = class {
|
|
1865
|
+
constructor(client, options) {
|
|
1866
|
+
this.client = client;
|
|
1867
|
+
this.options = options;
|
|
1868
|
+
this.memoryKey = options.memoryKey || "history";
|
|
1869
|
+
this.memoryKeys = [this.memoryKey];
|
|
1870
|
+
}
|
|
1871
|
+
memoryKeys;
|
|
1872
|
+
memoryKey;
|
|
1873
|
+
async loadMemoryVariables(_inputValues) {
|
|
1874
|
+
const response = await this.client.memory.search({
|
|
1875
|
+
project: this.options.project,
|
|
1876
|
+
query: "recent context",
|
|
1877
|
+
user_id: this.options.userId,
|
|
1878
|
+
session_id: this.options.sessionId,
|
|
1879
|
+
top_k: this.options.topK || 10,
|
|
1880
|
+
profile: this.options.profile || "fast",
|
|
1881
|
+
include_pending: true
|
|
1882
|
+
});
|
|
1883
|
+
const content = (response.results || []).map((row) => row.memory?.content || "").filter(Boolean).join("\n");
|
|
1884
|
+
return {
|
|
1885
|
+
[this.memoryKey]: content
|
|
1886
|
+
};
|
|
1887
|
+
}
|
|
1888
|
+
async saveContext(inputValues, outputValues) {
|
|
1889
|
+
const userInput = typeof inputValues.input === "string" ? inputValues.input : JSON.stringify(inputValues);
|
|
1890
|
+
const output = typeof outputValues.output === "string" ? outputValues.output : JSON.stringify(outputValues);
|
|
1891
|
+
await this.client.memory.addBulk({
|
|
1892
|
+
project: this.options.project,
|
|
1893
|
+
write_mode: "async",
|
|
1894
|
+
memories: [
|
|
1895
|
+
{
|
|
1896
|
+
content: `user: ${userInput}`,
|
|
1897
|
+
memory_type: "event",
|
|
1898
|
+
user_id: this.options.userId,
|
|
1899
|
+
session_id: this.options.sessionId
|
|
1900
|
+
},
|
|
1901
|
+
{
|
|
1902
|
+
content: `assistant: ${output}`,
|
|
1903
|
+
memory_type: "event",
|
|
1904
|
+
user_id: this.options.userId,
|
|
1905
|
+
session_id: this.options.sessionId
|
|
1906
|
+
}
|
|
1907
|
+
]
|
|
1908
|
+
});
|
|
1909
|
+
}
|
|
1910
|
+
async clear() {
|
|
1911
|
+
const profile = await this.client.memory.getUserProfile({
|
|
1912
|
+
project: this.options.project,
|
|
1913
|
+
user_id: this.options.userId
|
|
1914
|
+
});
|
|
1915
|
+
await Promise.all(
|
|
1916
|
+
(profile.memories || []).map((memory) => String(memory.id || "")).filter(Boolean).map((id) => this.client.memory.delete(id))
|
|
1917
|
+
);
|
|
1918
|
+
}
|
|
1919
|
+
};
|
|
1920
|
+
function createLangChainMemoryAdapter(client, options) {
|
|
1921
|
+
return new LangChainMemoryAdapter(client, options);
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
// ../src/sdk/adapters/langgraph.ts
|
|
1925
|
+
function asRecord(value) {
|
|
1926
|
+
return value && typeof value === "object" ? value : {};
|
|
1927
|
+
}
|
|
1928
|
+
function text(value) {
|
|
1929
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
1930
|
+
}
|
|
1931
|
+
function isTupleLike(value) {
|
|
1932
|
+
if (!value || typeof value !== "object") return false;
|
|
1933
|
+
const tuple = value;
|
|
1934
|
+
const config = asRecord(tuple.config);
|
|
1935
|
+
const configurable = asRecord(config.configurable);
|
|
1936
|
+
return Boolean(
|
|
1937
|
+
typeof configurable.thread_id === "string" && tuple.checkpoint && typeof tuple.checkpoint === "object"
|
|
1938
|
+
);
|
|
1939
|
+
}
|
|
1940
|
+
function tupleTimestamp(tuple, fallback) {
|
|
1941
|
+
const checkpoint = asRecord(tuple.checkpoint);
|
|
1942
|
+
const metadata = asRecord(tuple.metadata);
|
|
1943
|
+
const sources = [
|
|
1944
|
+
checkpoint.updatedAt,
|
|
1945
|
+
checkpoint.updated_at,
|
|
1946
|
+
checkpoint.ts,
|
|
1947
|
+
checkpoint.timestamp,
|
|
1948
|
+
metadata.updatedAt,
|
|
1949
|
+
metadata.updated_at,
|
|
1950
|
+
metadata.ts,
|
|
1951
|
+
metadata.timestamp,
|
|
1952
|
+
fallback
|
|
1953
|
+
];
|
|
1954
|
+
for (const source of sources) {
|
|
1955
|
+
const parsed = source ? new Date(String(source)).getTime() : Number.NaN;
|
|
1956
|
+
if (!Number.isNaN(parsed) && parsed > 0) return parsed;
|
|
1957
|
+
}
|
|
1958
|
+
return 0;
|
|
1959
|
+
}
|
|
1960
|
+
function metadataMatches(target, filter) {
|
|
1961
|
+
if (!filter || Object.keys(filter).length === 0) return true;
|
|
1962
|
+
const value = target || {};
|
|
1963
|
+
return Object.entries(filter).every(([key, expected]) => {
|
|
1964
|
+
if (!(key in value)) return false;
|
|
1965
|
+
return JSON.stringify(value[key]) === JSON.stringify(expected);
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1968
|
+
var LangGraphCheckpointAdapter = class {
|
|
1969
|
+
constructor(client, options = {}) {
|
|
1970
|
+
this.client = client;
|
|
1971
|
+
this.options = options;
|
|
1972
|
+
}
|
|
1973
|
+
localByKey = /* @__PURE__ */ new Map();
|
|
1974
|
+
localByThread = /* @__PURE__ */ new Map();
|
|
1975
|
+
options;
|
|
1976
|
+
getUserId(threadId) {
|
|
1977
|
+
const prefix = this.options.userIdPrefix || "langgraph-thread";
|
|
1978
|
+
return `${prefix}:${threadId}`;
|
|
1979
|
+
}
|
|
1980
|
+
resolveCheckpointNs(config) {
|
|
1981
|
+
return config.configurable.checkpoint_ns || this.options.defaultCheckpointNs || "default";
|
|
1982
|
+
}
|
|
1983
|
+
makeLocalKey(threadId, checkpointNs, checkpointId) {
|
|
1984
|
+
return `${threadId}:${checkpointNs}:${checkpointId}`;
|
|
1985
|
+
}
|
|
1986
|
+
normalizeTuple(tuple) {
|
|
1987
|
+
const config = asRecord(tuple.config);
|
|
1988
|
+
const configurable = asRecord(config.configurable);
|
|
1989
|
+
const threadId = text(configurable.thread_id) || "";
|
|
1990
|
+
const checkpointNs = text(configurable.checkpoint_ns) || this.options.defaultCheckpointNs || "default";
|
|
1991
|
+
const checkpointId = text(configurable.checkpoint_id) || "";
|
|
1992
|
+
return {
|
|
1993
|
+
config: {
|
|
1994
|
+
configurable: {
|
|
1995
|
+
thread_id: threadId,
|
|
1996
|
+
checkpoint_ns: checkpointNs,
|
|
1997
|
+
checkpoint_id: checkpointId || void 0
|
|
1998
|
+
}
|
|
1999
|
+
},
|
|
2000
|
+
checkpoint: asRecord(tuple.checkpoint),
|
|
2001
|
+
metadata: asRecord(tuple.metadata),
|
|
2002
|
+
parent_config: tuple.parent_config ? {
|
|
2003
|
+
configurable: {
|
|
2004
|
+
thread_id: text(tuple.parent_config.configurable.thread_id) || "",
|
|
2005
|
+
checkpoint_ns: text(tuple.parent_config.configurable.checkpoint_ns) || checkpointNs,
|
|
2006
|
+
checkpoint_id: text(tuple.parent_config.configurable.checkpoint_id)
|
|
2007
|
+
}
|
|
2008
|
+
} : null
|
|
2009
|
+
};
|
|
2010
|
+
}
|
|
2011
|
+
parseCheckpointTupleFromRow(row) {
|
|
2012
|
+
const rowMetadata = asRecord(row.metadata);
|
|
2013
|
+
const marker = rowMetadata.langgraph_checkpoint === true;
|
|
2014
|
+
const rawContent = text(row.content) || "";
|
|
2015
|
+
if (!rawContent) return null;
|
|
2016
|
+
try {
|
|
2017
|
+
const parsed = JSON.parse(rawContent);
|
|
2018
|
+
let tuple = null;
|
|
2019
|
+
if (isTupleLike(parsed)) {
|
|
2020
|
+
tuple = this.normalizeTuple(parsed);
|
|
2021
|
+
} else {
|
|
2022
|
+
const wrapped = asRecord(parsed);
|
|
2023
|
+
if (isTupleLike(wrapped.tuple)) {
|
|
2024
|
+
tuple = this.normalizeTuple(wrapped.tuple);
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
if (!tuple) return null;
|
|
2028
|
+
const config = tuple.config.configurable;
|
|
2029
|
+
if (!config.thread_id) return null;
|
|
2030
|
+
if (!config.checkpoint_id) return null;
|
|
2031
|
+
if (!marker && rowMetadata.checkpoint_id !== config.checkpoint_id) {
|
|
2032
|
+
return null;
|
|
2033
|
+
}
|
|
2034
|
+
return {
|
|
2035
|
+
tuple,
|
|
2036
|
+
memoryId: text(row.id),
|
|
2037
|
+
createdAt: text(row.createdAt) || text(row.created_at),
|
|
2038
|
+
updatedAt: text(row.updatedAt) || text(row.updated_at)
|
|
2039
|
+
};
|
|
2040
|
+
} catch {
|
|
2041
|
+
return null;
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
upsertLocal(record) {
|
|
2045
|
+
const cfg = record.tuple.config.configurable;
|
|
2046
|
+
const key = this.makeLocalKey(cfg.thread_id, cfg.checkpoint_ns || "default", cfg.checkpoint_id || "");
|
|
2047
|
+
this.localByKey.set(key, record);
|
|
2048
|
+
if (!this.localByThread.has(cfg.thread_id)) {
|
|
2049
|
+
this.localByThread.set(cfg.thread_id, /* @__PURE__ */ new Set());
|
|
2050
|
+
}
|
|
2051
|
+
this.localByThread.get(cfg.thread_id).add(key);
|
|
2052
|
+
}
|
|
2053
|
+
mergeWithLocal(records, threadId) {
|
|
2054
|
+
const merged = /* @__PURE__ */ new Map();
|
|
2055
|
+
for (const record of records) {
|
|
2056
|
+
const cfg = record.tuple.config.configurable;
|
|
2057
|
+
const key = this.makeLocalKey(cfg.thread_id, cfg.checkpoint_ns || "default", cfg.checkpoint_id || "");
|
|
2058
|
+
merged.set(key, record);
|
|
2059
|
+
}
|
|
2060
|
+
const localKeys = this.localByThread.get(threadId);
|
|
2061
|
+
if (localKeys) {
|
|
2062
|
+
for (const key of localKeys) {
|
|
2063
|
+
const local = this.localByKey.get(key);
|
|
2064
|
+
if (local) {
|
|
2065
|
+
merged.set(key, local);
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
return Array.from(merged.values());
|
|
2070
|
+
}
|
|
2071
|
+
applyListFilters(records, options) {
|
|
2072
|
+
let filtered = records;
|
|
2073
|
+
if (options?.filter?.checkpointNs) {
|
|
2074
|
+
filtered = filtered.filter(
|
|
2075
|
+
(record) => (record.tuple.config.configurable.checkpoint_ns || "default") === options.filter.checkpointNs
|
|
2076
|
+
);
|
|
2077
|
+
}
|
|
2078
|
+
if (options?.filter?.metadata) {
|
|
2079
|
+
filtered = filtered.filter((record) => metadataMatches(record.tuple.metadata, options.filter.metadata));
|
|
2080
|
+
}
|
|
2081
|
+
if (options?.before?.checkpointId) {
|
|
2082
|
+
const beforeId = options.before.checkpointId;
|
|
2083
|
+
filtered = filtered.filter(
|
|
2084
|
+
(record) => record.tuple.config.configurable.checkpoint_id !== beforeId
|
|
2085
|
+
);
|
|
2086
|
+
}
|
|
2087
|
+
if (options?.before?.updatedAt) {
|
|
2088
|
+
const cutoff = new Date(options.before.updatedAt).getTime();
|
|
2089
|
+
if (!Number.isNaN(cutoff)) {
|
|
2090
|
+
filtered = filtered.filter((record) => {
|
|
2091
|
+
const value = tupleTimestamp(record.tuple, record.updatedAt || record.createdAt);
|
|
2092
|
+
return value < cutoff;
|
|
2093
|
+
});
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
const direction = options?.sort || "desc";
|
|
2097
|
+
filtered.sort((a, b) => {
|
|
2098
|
+
const ta = tupleTimestamp(a.tuple, a.updatedAt || a.createdAt);
|
|
2099
|
+
const tb = tupleTimestamp(b.tuple, b.updatedAt || b.createdAt);
|
|
2100
|
+
return direction === "asc" ? ta - tb : tb - ta;
|
|
2101
|
+
});
|
|
2102
|
+
if (options?.limit && options.limit > 0) {
|
|
2103
|
+
return filtered.slice(0, options.limit);
|
|
2104
|
+
}
|
|
2105
|
+
return filtered;
|
|
2106
|
+
}
|
|
2107
|
+
async fetchThreadRecords(threadId) {
|
|
2108
|
+
const profile = await this.client.memory.getUserProfile({
|
|
2109
|
+
project: this.options.project,
|
|
2110
|
+
user_id: this.getUserId(threadId),
|
|
2111
|
+
include_pending: true
|
|
2112
|
+
});
|
|
2113
|
+
const parsed = (profile.memories || []).map((row) => this.parseCheckpointTupleFromRow(row)).filter((value) => value !== null).filter((record) => record.tuple.config.configurable.thread_id === threadId);
|
|
2114
|
+
const merged = this.mergeWithLocal(parsed, threadId);
|
|
2115
|
+
for (const record of merged) {
|
|
2116
|
+
this.upsertLocal(record);
|
|
2117
|
+
}
|
|
2118
|
+
return merged;
|
|
2119
|
+
}
|
|
2120
|
+
async get(config) {
|
|
2121
|
+
const threadId = config.configurable.thread_id;
|
|
2122
|
+
const checkpointNs = this.resolveCheckpointNs(config);
|
|
2123
|
+
const checkpointId = config.configurable.checkpoint_id;
|
|
2124
|
+
if (checkpointId) {
|
|
2125
|
+
const local = this.localByKey.get(this.makeLocalKey(threadId, checkpointNs, checkpointId));
|
|
2126
|
+
if (local) return local.tuple;
|
|
2127
|
+
}
|
|
2128
|
+
const records = await this.fetchThreadRecords(threadId);
|
|
2129
|
+
const scoped = records.filter((record) => {
|
|
2130
|
+
const cfg = record.tuple.config.configurable;
|
|
2131
|
+
if ((cfg.checkpoint_ns || "default") !== checkpointNs) return false;
|
|
2132
|
+
if (!checkpointId) return true;
|
|
2133
|
+
return cfg.checkpoint_id === checkpointId;
|
|
2134
|
+
});
|
|
2135
|
+
if (scoped.length === 0) return void 0;
|
|
2136
|
+
scoped.sort((a, b) => {
|
|
2137
|
+
const ta = tupleTimestamp(a.tuple, a.updatedAt || a.createdAt);
|
|
2138
|
+
const tb = tupleTimestamp(b.tuple, b.updatedAt || b.createdAt);
|
|
2139
|
+
return tb - ta;
|
|
2140
|
+
});
|
|
2141
|
+
return scoped[0].tuple;
|
|
2142
|
+
}
|
|
2143
|
+
async put(config, checkpoint, metadata, parentConfig) {
|
|
2144
|
+
const threadId = config.configurable.thread_id;
|
|
2145
|
+
const checkpointNs = this.resolveCheckpointNs(config);
|
|
2146
|
+
const checkpointId = config.configurable.checkpoint_id || text(checkpoint.id) || `cp_${stableHash(JSON.stringify({
|
|
2147
|
+
threadId,
|
|
2148
|
+
checkpointNs,
|
|
2149
|
+
checkpoint,
|
|
2150
|
+
metadata: metadata || {},
|
|
2151
|
+
parentConfig: parentConfig || null
|
|
2152
|
+
}))}`;
|
|
2153
|
+
const tuple = this.normalizeTuple({
|
|
2154
|
+
config: {
|
|
2155
|
+
configurable: {
|
|
2156
|
+
thread_id: threadId,
|
|
2157
|
+
checkpoint_ns: checkpointNs,
|
|
2158
|
+
checkpoint_id: checkpointId
|
|
2159
|
+
}
|
|
2160
|
+
},
|
|
2161
|
+
checkpoint: {
|
|
2162
|
+
...checkpoint,
|
|
2163
|
+
id: checkpointId
|
|
2164
|
+
},
|
|
2165
|
+
metadata: {
|
|
2166
|
+
...metadata || {},
|
|
2167
|
+
checkpoint_ns: checkpointNs,
|
|
2168
|
+
written_at: nowIso()
|
|
2169
|
+
},
|
|
2170
|
+
parent_config: parentConfig ? this.normalizeTuple({
|
|
2171
|
+
config: parentConfig,
|
|
2172
|
+
checkpoint: {},
|
|
2173
|
+
metadata: {},
|
|
2174
|
+
parent_config: null
|
|
2175
|
+
}).config : null
|
|
2176
|
+
});
|
|
2177
|
+
const record = { tuple, updatedAt: nowIso() };
|
|
2178
|
+
this.upsertLocal(record);
|
|
2179
|
+
await this.client.memory.add({
|
|
2180
|
+
project: this.options.project,
|
|
2181
|
+
user_id: this.getUserId(threadId),
|
|
2182
|
+
session_id: threadId,
|
|
2183
|
+
memory_type: "event",
|
|
2184
|
+
write_mode: "async",
|
|
2185
|
+
content: JSON.stringify(tuple),
|
|
2186
|
+
metadata: {
|
|
2187
|
+
langgraph_checkpoint: true,
|
|
2188
|
+
thread_id: threadId,
|
|
2189
|
+
checkpoint_ns: checkpointNs,
|
|
2190
|
+
checkpoint_id: checkpointId,
|
|
2191
|
+
parent_checkpoint_id: parentConfig?.configurable.checkpoint_id
|
|
2192
|
+
}
|
|
2193
|
+
});
|
|
2194
|
+
return tuple.config;
|
|
2195
|
+
}
|
|
2196
|
+
async list(config, options) {
|
|
2197
|
+
const threadId = config.configurable.thread_id;
|
|
2198
|
+
const checkpointNs = this.resolveCheckpointNs(config);
|
|
2199
|
+
const records = await this.fetchThreadRecords(threadId);
|
|
2200
|
+
const scoped = records.filter(
|
|
2201
|
+
(record) => (record.tuple.config.configurable.checkpoint_ns || "default") === checkpointNs
|
|
2202
|
+
);
|
|
2203
|
+
return this.applyListFilters(scoped, options).map((record) => record.tuple);
|
|
2204
|
+
}
|
|
2205
|
+
async search(params) {
|
|
2206
|
+
const includePending = params.includePending !== false;
|
|
2207
|
+
const profile = params.profile || "fast";
|
|
2208
|
+
const checkpointNs = params.checkpointNs || this.options.defaultCheckpointNs || "default";
|
|
2209
|
+
const response = await this.client.memory.search({
|
|
2210
|
+
project: this.options.project,
|
|
2211
|
+
query: params.query,
|
|
2212
|
+
user_id: this.getUserId(params.threadId),
|
|
2213
|
+
session_id: params.threadId,
|
|
2214
|
+
top_k: params.topK || 10,
|
|
2215
|
+
include_pending: includePending,
|
|
2216
|
+
profile
|
|
2217
|
+
});
|
|
2218
|
+
const serverHits = (response.results || []).map((row) => {
|
|
2219
|
+
const content = text(row.memory?.content);
|
|
2220
|
+
if (!content) return null;
|
|
2221
|
+
try {
|
|
2222
|
+
const parsed = JSON.parse(content);
|
|
2223
|
+
if (!isTupleLike(parsed)) return null;
|
|
2224
|
+
const tuple = this.normalizeTuple(parsed);
|
|
2225
|
+
const ns = tuple.config.configurable.checkpoint_ns || "default";
|
|
2226
|
+
if (tuple.config.configurable.thread_id !== params.threadId) return null;
|
|
2227
|
+
if (ns !== checkpointNs) return null;
|
|
2228
|
+
return { tuple, score: row.similarity || 0 };
|
|
2229
|
+
} catch {
|
|
2230
|
+
return null;
|
|
2231
|
+
}
|
|
2232
|
+
}).filter((value) => value !== null);
|
|
2233
|
+
const merged = /* @__PURE__ */ new Map();
|
|
2234
|
+
for (const hit of serverHits) {
|
|
2235
|
+
const cfg = hit.tuple.config.configurable;
|
|
2236
|
+
const key = this.makeLocalKey(cfg.thread_id, cfg.checkpoint_ns || "default", cfg.checkpoint_id || "");
|
|
2237
|
+
merged.set(key, hit);
|
|
2238
|
+
}
|
|
2239
|
+
const localKeys = this.localByThread.get(params.threadId);
|
|
2240
|
+
if (localKeys && includePending) {
|
|
2241
|
+
for (const key of localKeys) {
|
|
2242
|
+
const local = this.localByKey.get(key);
|
|
2243
|
+
if (!local) continue;
|
|
2244
|
+
const cfg = local.tuple.config.configurable;
|
|
2245
|
+
if ((cfg.checkpoint_ns || "default") !== checkpointNs) continue;
|
|
2246
|
+
if (!merged.has(key)) {
|
|
2247
|
+
merged.set(key, { tuple: local.tuple, score: 0 });
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
return Array.from(merged.values()).sort((a, b) => b.score - a.score).map((row) => row.tuple).slice(0, params.topK || 10);
|
|
2252
|
+
}
|
|
2253
|
+
};
|
|
2254
|
+
function createLangGraphCheckpointAdapter(client, options = {}) {
|
|
2255
|
+
return new LangGraphCheckpointAdapter(client, options);
|
|
2256
|
+
}
|
|
2257
|
+
|
|
376
2258
|
// ../src/sdk/graph-utils.ts
|
|
377
2259
|
function sanitizeId(id) {
|
|
378
2260
|
return `n_${id.replace(/[^a-zA-Z0-9_]/g, "_")}`;
|
|
379
2261
|
}
|
|
380
2262
|
function shortLabel(input, max = 48) {
|
|
381
|
-
const
|
|
382
|
-
if (
|
|
383
|
-
return `${
|
|
2263
|
+
const text2 = (input || "").replace(/\s+/g, " ").trim();
|
|
2264
|
+
if (text2.length <= max) return text2;
|
|
2265
|
+
return `${text2.slice(0, max - 3)}...`;
|
|
384
2266
|
}
|
|
385
2267
|
function memoryGraphToMermaid(graph) {
|
|
386
2268
|
const lines = ["flowchart LR"];
|
|
@@ -418,24 +2300,25 @@ var DEFAULT_BASE_DELAY_MS = 250;
|
|
|
418
2300
|
var DEFAULT_MAX_DELAY_MS = 2e3;
|
|
419
2301
|
var DEFAULT_TIMEOUT_MS = 15e3;
|
|
420
2302
|
var PROJECT_CACHE_TTL_MS = 3e4;
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
2303
|
+
var DEPRECATION_WARNINGS2 = /* @__PURE__ */ new Set();
|
|
2304
|
+
function warnDeprecatedOnce2(key, message) {
|
|
2305
|
+
if (DEPRECATION_WARNINGS2.has(key)) return;
|
|
2306
|
+
DEPRECATION_WARNINGS2.add(key);
|
|
2307
|
+
if (typeof console !== "undefined" && typeof console.warn === "function") {
|
|
2308
|
+
console.warn(message);
|
|
2309
|
+
}
|
|
427
2310
|
}
|
|
428
2311
|
function isLikelyProjectId(projectRef) {
|
|
429
2312
|
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);
|
|
430
2313
|
}
|
|
431
|
-
function
|
|
2314
|
+
function normalizeBaseUrl2(url) {
|
|
432
2315
|
let normalized = url.trim().replace(/\/+$/, "");
|
|
433
2316
|
normalized = normalized.replace(/\/api\/v1$/i, "");
|
|
434
2317
|
normalized = normalized.replace(/\/v1$/i, "");
|
|
435
2318
|
normalized = normalized.replace(/\/api$/i, "");
|
|
436
2319
|
return normalized;
|
|
437
2320
|
}
|
|
438
|
-
function
|
|
2321
|
+
function normalizeEndpoint2(endpoint) {
|
|
439
2322
|
const withLeadingSlash = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
|
|
440
2323
|
if (/^\/api\/v1(\/|$)/i.test(withLeadingSlash)) {
|
|
441
2324
|
return withLeadingSlash.replace(/^\/api/i, "");
|
|
@@ -452,6 +2335,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
452
2335
|
defaultProject;
|
|
453
2336
|
timeoutMs;
|
|
454
2337
|
retryConfig;
|
|
2338
|
+
runtimeClient;
|
|
455
2339
|
projectRefToId = /* @__PURE__ */ new Map();
|
|
456
2340
|
projectCache = [];
|
|
457
2341
|
projectCacheExpiresAt = 0;
|
|
@@ -463,7 +2347,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
463
2347
|
});
|
|
464
2348
|
}
|
|
465
2349
|
this.apiKey = config.apiKey;
|
|
466
|
-
this.baseUrl =
|
|
2350
|
+
this.baseUrl = normalizeBaseUrl2(config.baseUrl || "https://context.usewhisper.dev");
|
|
467
2351
|
this.defaultProject = config.project;
|
|
468
2352
|
this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
469
2353
|
this.retryConfig = {
|
|
@@ -471,6 +2355,35 @@ var WhisperContext = class _WhisperContext {
|
|
|
471
2355
|
baseDelayMs: config.retry?.baseDelayMs ?? DEFAULT_BASE_DELAY_MS,
|
|
472
2356
|
maxDelayMs: config.retry?.maxDelayMs ?? DEFAULT_MAX_DELAY_MS
|
|
473
2357
|
};
|
|
2358
|
+
this.runtimeClient = new RuntimeClient({
|
|
2359
|
+
apiKey: this.apiKey,
|
|
2360
|
+
baseUrl: this.baseUrl,
|
|
2361
|
+
compatMode: "fallback",
|
|
2362
|
+
timeouts: {
|
|
2363
|
+
searchMs: this.timeoutMs,
|
|
2364
|
+
writeAckMs: this.timeoutMs,
|
|
2365
|
+
bulkMs: Math.max(this.timeoutMs, 1e4),
|
|
2366
|
+
profileMs: this.timeoutMs,
|
|
2367
|
+
sessionMs: this.timeoutMs
|
|
2368
|
+
},
|
|
2369
|
+
retryPolicy: {
|
|
2370
|
+
baseBackoffMs: this.retryConfig.baseDelayMs,
|
|
2371
|
+
maxBackoffMs: this.retryConfig.maxDelayMs,
|
|
2372
|
+
maxAttemptsByOperation: {
|
|
2373
|
+
search: this.retryConfig.maxAttempts,
|
|
2374
|
+
writeAck: this.retryConfig.maxAttempts,
|
|
2375
|
+
bulk: this.retryConfig.maxAttempts,
|
|
2376
|
+
profile: this.retryConfig.maxAttempts,
|
|
2377
|
+
session: this.retryConfig.maxAttempts,
|
|
2378
|
+
query: this.retryConfig.maxAttempts,
|
|
2379
|
+
get: this.retryConfig.maxAttempts
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
});
|
|
2383
|
+
warnDeprecatedOnce2(
|
|
2384
|
+
"whisper_context_class",
|
|
2385
|
+
"[Whisper SDK] WhisperContext is deprecated in v3 and scheduled for removal in v4. Prefer WhisperClient for runtime features and future contract compatibility."
|
|
2386
|
+
);
|
|
474
2387
|
}
|
|
475
2388
|
withProject(project) {
|
|
476
2389
|
return new _WhisperContext({
|
|
@@ -609,76 +2522,58 @@ var WhisperContext = class _WhisperContext {
|
|
|
609
2522
|
const message = (error.message || "").toLowerCase();
|
|
610
2523
|
return !isProjectNotFoundMessage(message);
|
|
611
2524
|
}
|
|
2525
|
+
inferOperation(endpoint, method) {
|
|
2526
|
+
const normalized = normalizeEndpoint2(endpoint).toLowerCase();
|
|
2527
|
+
if (normalized.includes("/memory/search")) return "search";
|
|
2528
|
+
if (normalized.includes("/memory/bulk")) return "bulk";
|
|
2529
|
+
if (normalized.includes("/memory/profile") || normalized.includes("/memory/session")) return "profile";
|
|
2530
|
+
if (normalized.includes("/memory/ingest/session")) return "session";
|
|
2531
|
+
if (normalized.includes("/context/query")) return "query";
|
|
2532
|
+
if (method === "GET") return "get";
|
|
2533
|
+
return "writeAck";
|
|
2534
|
+
}
|
|
612
2535
|
async request(endpoint, options = {}) {
|
|
613
|
-
const
|
|
614
|
-
const normalizedEndpoint =
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
2536
|
+
const method = String(options.method || "GET").toUpperCase();
|
|
2537
|
+
const normalizedEndpoint = normalizeEndpoint2(endpoint);
|
|
2538
|
+
const operation = this.inferOperation(normalizedEndpoint, method);
|
|
2539
|
+
let body;
|
|
2540
|
+
if (typeof options.body === "string") {
|
|
619
2541
|
try {
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
try {
|
|
641
|
-
payload = await response.json();
|
|
642
|
-
} catch {
|
|
643
|
-
payload = await response.text().catch(() => "");
|
|
644
|
-
}
|
|
645
|
-
let message = typeof payload === "string" ? payload : payload?.error || payload?.message || `HTTP ${response.status}: ${response.statusText}`;
|
|
646
|
-
if (response.status === 404 && !isProjectNotFoundMessage(message)) {
|
|
647
|
-
const endpointHint = `${this.baseUrl}${normalizedEndpoint}`;
|
|
648
|
-
message = `Endpoint not found at ${endpointHint}. This deployment may not support this API route.`;
|
|
649
|
-
}
|
|
650
|
-
const { code, retryable } = this.classifyError(response.status, message);
|
|
651
|
-
const err = new WhisperError({
|
|
652
|
-
code,
|
|
653
|
-
message,
|
|
654
|
-
status: response.status,
|
|
655
|
-
retryable,
|
|
656
|
-
details: payload
|
|
657
|
-
});
|
|
658
|
-
if (!retryable || attempt === maxAttempts - 1) {
|
|
659
|
-
throw err;
|
|
660
|
-
}
|
|
661
|
-
await sleep(getBackoffDelay(attempt, this.retryConfig.baseDelayMs, this.retryConfig.maxDelayMs));
|
|
662
|
-
continue;
|
|
663
|
-
}
|
|
664
|
-
return response.json();
|
|
665
|
-
} catch (error) {
|
|
666
|
-
clearTimeout(timeout);
|
|
667
|
-
const isAbort = error?.name === "AbortError";
|
|
668
|
-
const mapped = error instanceof WhisperError ? error : new WhisperError({
|
|
669
|
-
code: isAbort ? "TIMEOUT" : "NETWORK_ERROR",
|
|
670
|
-
message: isAbort ? "Request timed out" : error?.message || "Network request failed",
|
|
671
|
-
retryable: true,
|
|
672
|
-
details: error
|
|
673
|
-
});
|
|
674
|
-
lastError = mapped;
|
|
675
|
-
if (!mapped.retryable || attempt === maxAttempts - 1) {
|
|
676
|
-
throw mapped;
|
|
677
|
-
}
|
|
678
|
-
await sleep(getBackoffDelay(attempt, this.retryConfig.baseDelayMs, this.retryConfig.maxDelayMs));
|
|
2542
|
+
body = JSON.parse(options.body);
|
|
2543
|
+
} catch {
|
|
2544
|
+
body = void 0;
|
|
2545
|
+
}
|
|
2546
|
+
} 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)) {
|
|
2547
|
+
body = options.body;
|
|
2548
|
+
}
|
|
2549
|
+
try {
|
|
2550
|
+
const response = await this.runtimeClient.request({
|
|
2551
|
+
endpoint: normalizedEndpoint,
|
|
2552
|
+
method,
|
|
2553
|
+
operation,
|
|
2554
|
+
idempotent: method === "GET" || method === "POST" && (operation === "search" || operation === "query" || operation === "profile"),
|
|
2555
|
+
body,
|
|
2556
|
+
headers: options.headers || {}
|
|
2557
|
+
});
|
|
2558
|
+
return response.data;
|
|
2559
|
+
} catch (error) {
|
|
2560
|
+
if (!(error instanceof RuntimeClientError)) {
|
|
2561
|
+
throw error;
|
|
679
2562
|
}
|
|
2563
|
+
let message = error.message;
|
|
2564
|
+
if (error.status === 404 && !isProjectNotFoundMessage(message)) {
|
|
2565
|
+
const endpointHint = `${this.baseUrl}${normalizedEndpoint}`;
|
|
2566
|
+
message = `Endpoint not found at ${endpointHint}. This deployment may not support this API route.`;
|
|
2567
|
+
}
|
|
2568
|
+
const { code, retryable } = this.classifyError(error.status, message);
|
|
2569
|
+
throw new WhisperError({
|
|
2570
|
+
code,
|
|
2571
|
+
message,
|
|
2572
|
+
status: error.status,
|
|
2573
|
+
retryable,
|
|
2574
|
+
details: error.details
|
|
2575
|
+
});
|
|
680
2576
|
}
|
|
681
|
-
throw lastError instanceof Error ? lastError : new WhisperError({ code: "REQUEST_FAILED", message: "Request failed" });
|
|
682
2577
|
}
|
|
683
2578
|
async query(params) {
|
|
684
2579
|
const projectRef = this.getRequiredProject(params.project);
|
|
@@ -731,6 +2626,69 @@ var WhisperContext = class _WhisperContext {
|
|
|
731
2626
|
async syncSource(sourceId) {
|
|
732
2627
|
return this.request(`/v1/sources/${sourceId}/sync`, { method: "POST" });
|
|
733
2628
|
}
|
|
2629
|
+
async addSourceByType(projectId, params) {
|
|
2630
|
+
const resolvedProjectId = await this.resolveProjectId(projectId);
|
|
2631
|
+
return this.request(`/v1/projects/${resolvedProjectId}/add_source`, {
|
|
2632
|
+
method: "POST",
|
|
2633
|
+
body: JSON.stringify(params)
|
|
2634
|
+
});
|
|
2635
|
+
}
|
|
2636
|
+
async getSourceStatus(sourceId) {
|
|
2637
|
+
return this.request(`/v1/sources/${sourceId}/status`, { method: "GET" });
|
|
2638
|
+
}
|
|
2639
|
+
async createCanonicalSource(project, params) {
|
|
2640
|
+
const connector_type = params.type === "github" ? "github" : params.type === "web" ? "website" : params.type === "pdf" ? "pdf" : params.type === "local" ? "local-folder" : "slack";
|
|
2641
|
+
const config = {};
|
|
2642
|
+
if (params.type === "github") {
|
|
2643
|
+
if (!params.owner || !params.repo) throw new WhisperError({ code: "REQUEST_FAILED", message: "github source requires owner and repo" });
|
|
2644
|
+
config.owner = params.owner;
|
|
2645
|
+
config.repo = params.repo;
|
|
2646
|
+
if (params.branch) config.branch = params.branch;
|
|
2647
|
+
if (params.paths) config.paths = params.paths;
|
|
2648
|
+
} else if (params.type === "web") {
|
|
2649
|
+
if (!params.url) throw new WhisperError({ code: "REQUEST_FAILED", message: "web source requires url" });
|
|
2650
|
+
config.url = params.url;
|
|
2651
|
+
if (params.crawl_depth !== void 0) config.crawl_depth = params.crawl_depth;
|
|
2652
|
+
if (params.include_paths) config.include_paths = params.include_paths;
|
|
2653
|
+
if (params.exclude_paths) config.exclude_paths = params.exclude_paths;
|
|
2654
|
+
} else if (params.type === "pdf") {
|
|
2655
|
+
if (!params.url && !params.file_path) throw new WhisperError({ code: "REQUEST_FAILED", message: "pdf source requires url or file_path" });
|
|
2656
|
+
if (params.url) config.url = params.url;
|
|
2657
|
+
if (params.file_path) config.file_path = params.file_path;
|
|
2658
|
+
} else if (params.type === "local") {
|
|
2659
|
+
if (!params.path) throw new WhisperError({ code: "REQUEST_FAILED", message: "local source requires path" });
|
|
2660
|
+
config.path = params.path;
|
|
2661
|
+
if (params.glob) config.glob = params.glob;
|
|
2662
|
+
if (params.max_files !== void 0) config.max_files = params.max_files;
|
|
2663
|
+
} else {
|
|
2664
|
+
config.channel_ids = params.channel_ids || [];
|
|
2665
|
+
if (params.since) config.since = params.since;
|
|
2666
|
+
if (params.workspace_id) config.workspace_id = params.workspace_id;
|
|
2667
|
+
if (params.token) config.token = params.token;
|
|
2668
|
+
if (params.auth_ref) config.auth_ref = params.auth_ref;
|
|
2669
|
+
}
|
|
2670
|
+
if (params.metadata) config.metadata = params.metadata;
|
|
2671
|
+
config.auto_index = params.auto_index ?? true;
|
|
2672
|
+
const created = await this.addSource(project, {
|
|
2673
|
+
name: params.name || `${params.type}-source-${Date.now()}`,
|
|
2674
|
+
connector_type,
|
|
2675
|
+
config
|
|
2676
|
+
});
|
|
2677
|
+
let status = "queued";
|
|
2678
|
+
let jobId = null;
|
|
2679
|
+
if (params.auto_index ?? true) {
|
|
2680
|
+
const syncRes = await this.syncSource(created.id);
|
|
2681
|
+
status = "indexing";
|
|
2682
|
+
jobId = String(syncRes?.id || syncRes?.job_id || "");
|
|
2683
|
+
}
|
|
2684
|
+
return {
|
|
2685
|
+
source_id: created.id,
|
|
2686
|
+
status,
|
|
2687
|
+
job_id: jobId,
|
|
2688
|
+
index_started: params.auto_index ?? true,
|
|
2689
|
+
warnings: []
|
|
2690
|
+
};
|
|
2691
|
+
}
|
|
734
2692
|
async ingest(projectId, documents) {
|
|
735
2693
|
const resolvedProjectId = await this.resolveProjectId(projectId);
|
|
736
2694
|
return this.request(`/v1/projects/${resolvedProjectId}/ingest`, {
|
|
@@ -751,7 +2709,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
751
2709
|
async addMemory(params) {
|
|
752
2710
|
const projectRef = this.getRequiredProject(params.project);
|
|
753
2711
|
return this.withProjectRefFallback(projectRef, async (project) => {
|
|
754
|
-
const
|
|
2712
|
+
const toSotaType2 = (memoryType) => {
|
|
755
2713
|
switch (memoryType) {
|
|
756
2714
|
case "episodic":
|
|
757
2715
|
return "event";
|
|
@@ -763,7 +2721,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
763
2721
|
return memoryType;
|
|
764
2722
|
}
|
|
765
2723
|
};
|
|
766
|
-
const
|
|
2724
|
+
const toLegacyType2 = (memoryType) => {
|
|
767
2725
|
switch (memoryType) {
|
|
768
2726
|
case "event":
|
|
769
2727
|
return "episodic";
|
|
@@ -784,7 +2742,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
784
2742
|
body: JSON.stringify({
|
|
785
2743
|
project,
|
|
786
2744
|
content: params.content,
|
|
787
|
-
memory_type:
|
|
2745
|
+
memory_type: toSotaType2(params.memory_type),
|
|
788
2746
|
user_id: params.user_id,
|
|
789
2747
|
session_id: params.session_id,
|
|
790
2748
|
agent_id: params.agent_id,
|
|
@@ -811,7 +2769,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
811
2769
|
body: JSON.stringify({
|
|
812
2770
|
project,
|
|
813
2771
|
content: params.content,
|
|
814
|
-
memory_type:
|
|
2772
|
+
memory_type: toLegacyType2(params.memory_type),
|
|
815
2773
|
user_id: params.user_id,
|
|
816
2774
|
session_id: params.session_id,
|
|
817
2775
|
agent_id: params.agent_id,
|
|
@@ -1026,6 +2984,16 @@ var WhisperContext = class _WhisperContext {
|
|
|
1026
2984
|
}
|
|
1027
2985
|
});
|
|
1028
2986
|
}
|
|
2987
|
+
async getMemory(memoryId) {
|
|
2988
|
+
try {
|
|
2989
|
+
return await this.request(`/v1/memory/${memoryId}`);
|
|
2990
|
+
} catch (error) {
|
|
2991
|
+
if (!this.isEndpointNotFoundError(error)) {
|
|
2992
|
+
throw error;
|
|
2993
|
+
}
|
|
2994
|
+
return this.request(`/v1/memories/${memoryId}`);
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
1029
2997
|
async getMemoryVersions(memoryId) {
|
|
1030
2998
|
return this.request(`/v1/memory/${memoryId}/versions`);
|
|
1031
2999
|
}
|
|
@@ -1208,8 +3176,11 @@ var WhisperContext = class _WhisperContext {
|
|
|
1208
3176
|
};
|
|
1209
3177
|
sources = {
|
|
1210
3178
|
add: (projectId, params) => this.addSource(projectId, params),
|
|
3179
|
+
addSource: (projectId, params) => this.addSourceByType(projectId, params),
|
|
1211
3180
|
sync: (sourceId) => this.syncSource(sourceId),
|
|
1212
|
-
syncSource: (sourceId) => this.syncSource(sourceId)
|
|
3181
|
+
syncSource: (sourceId) => this.syncSource(sourceId),
|
|
3182
|
+
status: (sourceId) => this.getSourceStatus(sourceId),
|
|
3183
|
+
getStatus: (sourceId) => this.getSourceStatus(sourceId)
|
|
1213
3184
|
};
|
|
1214
3185
|
memory = {
|
|
1215
3186
|
add: (params) => this.addMemory(params),
|
|
@@ -1221,6 +3192,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
1221
3192
|
ingestSession: (params) => this.ingestSession(params),
|
|
1222
3193
|
getSessionMemories: (params) => this.getSessionMemories(params),
|
|
1223
3194
|
getUserProfile: (params) => this.getUserProfile(params),
|
|
3195
|
+
get: (memoryId) => this.getMemory(memoryId),
|
|
1224
3196
|
getVersions: (memoryId) => this.getMemoryVersions(memoryId),
|
|
1225
3197
|
update: (memoryId, params) => this.updateMemory(memoryId, params),
|
|
1226
3198
|
delete: (memoryId) => this.deleteMemory(memoryId),
|
|
@@ -1256,11 +3228,17 @@ var WhisperContext = class _WhisperContext {
|
|
|
1256
3228
|
var index_default = WhisperContext;
|
|
1257
3229
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1258
3230
|
0 && (module.exports = {
|
|
3231
|
+
LangChainMemoryAdapter,
|
|
3232
|
+
LangGraphCheckpointAdapter,
|
|
1259
3233
|
Whisper,
|
|
1260
3234
|
WhisperAgentMiddleware,
|
|
3235
|
+
WhisperClient,
|
|
1261
3236
|
WhisperContext,
|
|
1262
3237
|
WhisperDefault,
|
|
1263
3238
|
WhisperError,
|
|
3239
|
+
WhisperRuntimeClient,
|
|
1264
3240
|
createAgentMiddleware,
|
|
3241
|
+
createLangChainMemoryAdapter,
|
|
3242
|
+
createLangGraphCheckpointAdapter,
|
|
1265
3243
|
memoryGraphToMermaid
|
|
1266
3244
|
});
|