@usewhisper/sdk 1.1.0 → 2.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.
Files changed (6) hide show
  1. package/README.md +256 -256
  2. package/index.d.mts +311 -3
  3. package/index.d.ts +311 -3
  4. package/index.js +388 -12
  5. package/index.mjs +381 -11
  6. package/package.json +56 -56
package/index.js CHANGED
@@ -20,11 +20,333 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // ../src/sdk/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ Whisper: () => Whisper,
24
+ WhisperAgentMiddleware: () => WhisperAgentMiddleware,
23
25
  WhisperContext: () => WhisperContext,
26
+ WhisperDefault: () => whisper_agent_default,
24
27
  WhisperError: () => WhisperError,
28
+ createAgentMiddleware: () => createAgentMiddleware,
25
29
  default: () => index_default
26
30
  });
27
31
  module.exports = __toCommonJS(index_exports);
32
+
33
+ // ../src/sdk/whisper-agent.ts
34
+ var Whisper = class {
35
+ client;
36
+ options;
37
+ sessionId;
38
+ userId;
39
+ constructor(options) {
40
+ if (!options.apiKey) {
41
+ throw new Error("API key is required");
42
+ }
43
+ const clientConfig = {
44
+ apiKey: options.apiKey,
45
+ baseUrl: options.baseUrl,
46
+ project: options.project || "default"
47
+ };
48
+ if (options.timeoutMs) clientConfig.timeoutMs = options.timeoutMs;
49
+ if (options.retry) clientConfig.retry = options.retry;
50
+ this.client = new WhisperContext(clientConfig);
51
+ const finalRetry = options.retry || { maxAttempts: 3, baseDelayMs: 250, maxDelayMs: 2e3 };
52
+ this.options = {
53
+ apiKey: options.apiKey,
54
+ baseUrl: options.baseUrl || "https://context.usewhisper.dev",
55
+ project: options.project || "default",
56
+ timeoutMs: options.timeoutMs || 15e3,
57
+ retry: finalRetry,
58
+ contextLimit: options.contextLimit ?? 10,
59
+ memoryTypes: options.memoryTypes ?? ["factual", "preference", "event", "goal", "relationship", "opinion", "instruction"],
60
+ contextPrefix: options.contextPrefix ?? "Relevant context:",
61
+ autoExtract: options.autoExtract ?? true,
62
+ autoExtractMinConfidence: options.autoExtractMinConfidence ?? 0.65,
63
+ maxMemoriesPerCapture: options.maxMemoriesPerCapture ?? 5
64
+ };
65
+ }
66
+ /**
67
+ * Set session ID for conversation tracking
68
+ */
69
+ session(sessionId) {
70
+ this.sessionId = sessionId;
71
+ return this;
72
+ }
73
+ /**
74
+ * Set user ID for user-specific memories
75
+ */
76
+ user(userId) {
77
+ this.userId = userId;
78
+ return this;
79
+ }
80
+ /**
81
+ * Get relevant context BEFORE your LLM call
82
+ *
83
+ * @param query - What you want to know / user question
84
+ * @returns Context string and raw results
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * const { context, results, count } = await whisper.getContext(
89
+ * "What are user's preferences?",
90
+ * { userId: "user-123" }
91
+ * );
92
+ *
93
+ * // Results: [
94
+ * // { content: "User prefers dark mode", type: "preference", score: 0.95 },
95
+ * // { content: "Allergic to nuts", type: "factual", score: 0.89 }
96
+ * // ]
97
+ * ```
98
+ */
99
+ async getContext(query, options) {
100
+ const result = await this.client.query({
101
+ project: options?.project ?? this.options.project,
102
+ query,
103
+ top_k: options?.limit ?? this.options.contextLimit,
104
+ include_memories: true,
105
+ user_id: options?.userId ?? this.userId,
106
+ session_id: options?.sessionId ?? this.sessionId
107
+ });
108
+ const context = result.results.map((r, i) => `[${i + 1}] ${r.content}`).join("\n");
109
+ return {
110
+ context: context ? `${this.options.contextPrefix}
111
+ ${context}` : "",
112
+ results: result.results,
113
+ count: result.meta.total
114
+ };
115
+ }
116
+ /**
117
+ * Remember what happened AFTER your LLM response
118
+ *
119
+ * Fire-and-forget - doesn't block your response
120
+ *
121
+ * @param content - What your LLM responded with
122
+ * @returns Promise that resolves when stored (or fails silently)
123
+ *
124
+ * @example
125
+ * ```typescript
126
+ * const llmResponse = "I've set your theme to dark mode and removed nuts from recommendations.";
127
+ *
128
+ * await whisper.remember(llmResponse, { userId: "user-123" });
129
+ * // → Auto-extracts: "theme set to dark mode", "nut allergy"
130
+ * // → Stored as preferences
131
+ * ```
132
+ */
133
+ async remember(content, options) {
134
+ if (!content || content.length < 5) {
135
+ return { success: false };
136
+ }
137
+ try {
138
+ if (this.options.autoExtract) {
139
+ const extraction = await this.client.extractMemories({
140
+ project: options?.project ?? this.options.project,
141
+ message: content,
142
+ user_id: options?.userId ?? this.userId,
143
+ session_id: options?.sessionId ?? this.sessionId,
144
+ enable_pattern: true,
145
+ enable_inference: true,
146
+ min_confidence: this.options.autoExtractMinConfidence
147
+ });
148
+ const extractedMemories = (extraction.all || []).filter((m) => (m.confidence || 0) >= this.options.autoExtractMinConfidence).slice(0, this.options.maxMemoriesPerCapture);
149
+ if (extractedMemories.length > 0) {
150
+ const bulk = await this.client.addMemoriesBulk({
151
+ project: options?.project ?? this.options.project,
152
+ async: false,
153
+ memories: extractedMemories.map((m) => ({
154
+ content: m.content,
155
+ memory_type: m.memoryType,
156
+ user_id: options?.userId ?? this.userId,
157
+ session_id: options?.sessionId ?? this.sessionId,
158
+ importance: Math.max(0.5, Math.min(1, m.confidence || 0.7)),
159
+ confidence: m.confidence || 0.7,
160
+ entity_mentions: m.entityMentions || [],
161
+ event_date: m.eventDate || void 0,
162
+ metadata: {
163
+ extracted: true,
164
+ extraction_method: extraction.extractionMethod,
165
+ extraction_reasoning: m.reasoning,
166
+ inferred: Boolean(m.inferred)
167
+ }
168
+ }))
169
+ });
170
+ const memoryIds = this.extractMemoryIdsFromBulkResponse(bulk);
171
+ return {
172
+ success: true,
173
+ memoryId: memoryIds[0],
174
+ memoryIds: memoryIds.length > 0 ? memoryIds : void 0,
175
+ extracted: extractedMemories.length
176
+ };
177
+ }
178
+ }
179
+ const result = await this.client.addMemory({
180
+ project: options?.project ?? this.options.project,
181
+ content,
182
+ user_id: options?.userId ?? this.userId,
183
+ session_id: options?.sessionId ?? this.sessionId
184
+ });
185
+ return {
186
+ success: true,
187
+ memoryId: result?.id
188
+ };
189
+ } catch (error) {
190
+ console.error("[Whisper] Remember failed:", error);
191
+ return { success: false };
192
+ }
193
+ }
194
+ /**
195
+ * Alias for remember() - same thing
196
+ */
197
+ async capture(content, options) {
198
+ return this.remember(content, options);
199
+ }
200
+ /**
201
+ * Capture from multiple messages (e.g., full conversation)
202
+ */
203
+ async captureSession(messages, options) {
204
+ try {
205
+ const result = await this.client.ingestSession({
206
+ project: options?.project ?? this.options.project,
207
+ session_id: options?.sessionId ?? this.sessionId ?? "default",
208
+ user_id: options?.userId ?? this.userId,
209
+ messages: messages.filter((m) => m.role !== "system").map((m) => ({
210
+ role: m.role,
211
+ content: m.content,
212
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
213
+ }))
214
+ });
215
+ return {
216
+ success: true,
217
+ extracted: result?.memories_created ?? 0
218
+ };
219
+ } catch (error) {
220
+ console.error("[Whisper] Session capture failed:", error);
221
+ return { success: false, extracted: 0 };
222
+ }
223
+ }
224
+ /**
225
+ * Run a full agent turn with automatic memory read (before) + write (after).
226
+ */
227
+ async runTurn(params) {
228
+ const contextResult = await this.getContext(params.userMessage, {
229
+ userId: params.userId,
230
+ sessionId: params.sessionId,
231
+ project: params.project,
232
+ limit: params.limit
233
+ });
234
+ const prompt = contextResult.context ? `${contextResult.context}
235
+
236
+ User: ${params.userMessage}` : params.userMessage;
237
+ const response = await params.generate(prompt);
238
+ const captureResult = await this.captureSession(
239
+ [
240
+ { role: "user", content: params.userMessage },
241
+ { role: "assistant", content: response }
242
+ ],
243
+ {
244
+ userId: params.userId,
245
+ sessionId: params.sessionId,
246
+ project: params.project
247
+ }
248
+ );
249
+ return {
250
+ response,
251
+ context: contextResult.context,
252
+ count: contextResult.count,
253
+ extracted: captureResult.extracted
254
+ };
255
+ }
256
+ /**
257
+ * Direct access to WhisperContext for advanced usage
258
+ */
259
+ raw() {
260
+ return this.client;
261
+ }
262
+ extractMemoryIdsFromBulkResponse(bulkResponse) {
263
+ const ids = [];
264
+ if (Array.isArray(bulkResponse?.memories)) {
265
+ for (const memory of bulkResponse.memories) {
266
+ if (memory?.id) ids.push(memory.id);
267
+ }
268
+ }
269
+ if (bulkResponse?.memory?.id) {
270
+ ids.push(bulkResponse.memory.id);
271
+ }
272
+ if (bulkResponse?.id) {
273
+ ids.push(bulkResponse.id);
274
+ }
275
+ return Array.from(new Set(ids));
276
+ }
277
+ };
278
+ var whisper_agent_default = Whisper;
279
+
280
+ // ../src/sdk/middleware.ts
281
+ var WhisperAgentMiddleware = class {
282
+ whisper;
283
+ promptBuilder;
284
+ constructor(config) {
285
+ this.whisper = new Whisper(config);
286
+ this.promptBuilder = config.promptBuilder || (({ context, userMessage }) => {
287
+ if (!context) return userMessage;
288
+ return `${context}
289
+
290
+ User: ${userMessage}`;
291
+ });
292
+ }
293
+ async beforeTurn(params) {
294
+ const contextResult = await this.whisper.getContext(params.userMessage, {
295
+ userId: params.userId,
296
+ sessionId: params.sessionId,
297
+ project: params.project,
298
+ limit: params.contextLimit
299
+ });
300
+ const prompt = this.promptBuilder({
301
+ context: contextResult.context,
302
+ userMessage: params.userMessage
303
+ });
304
+ return {
305
+ prompt,
306
+ context: contextResult.context,
307
+ contextCount: contextResult.count
308
+ };
309
+ }
310
+ async afterTurn(params) {
311
+ return this.whisper.captureSession(
312
+ [
313
+ { role: "user", content: params.userMessage },
314
+ { role: "assistant", content: params.assistantMessage }
315
+ ],
316
+ {
317
+ userId: params.userId,
318
+ sessionId: params.sessionId,
319
+ project: params.project
320
+ }
321
+ );
322
+ }
323
+ async wrapGenerate(params) {
324
+ const before = await this.beforeTurn(params);
325
+ const response = await params.generate(before.prompt);
326
+ const after = await this.afterTurn({
327
+ userMessage: params.userMessage,
328
+ assistantMessage: response,
329
+ userId: params.userId,
330
+ sessionId: params.sessionId,
331
+ project: params.project
332
+ });
333
+ return {
334
+ response,
335
+ prompt: before.prompt,
336
+ context: before.context,
337
+ contextCount: before.contextCount,
338
+ extracted: after.extracted
339
+ };
340
+ }
341
+ raw() {
342
+ return this.whisper;
343
+ }
344
+ };
345
+ function createAgentMiddleware(config) {
346
+ return new WhisperAgentMiddleware(config);
347
+ }
348
+
349
+ // ../src/sdk/index.ts
28
350
  var WhisperError = class extends Error {
29
351
  code;
30
352
  status;
@@ -54,11 +376,24 @@ function getBackoffDelay(attempt, base, max) {
54
376
  function isLikelyProjectId(projectRef) {
55
377
  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);
56
378
  }
379
+ function normalizeBaseUrl(url) {
380
+ let normalized = url.trim().replace(/\/+$/, "");
381
+ normalized = normalized.replace(/\/api\/v1$/i, "");
382
+ normalized = normalized.replace(/\/v1$/i, "");
383
+ normalized = normalized.replace(/\/api$/i, "");
384
+ return normalized;
385
+ }
386
+ function normalizeEndpoint(endpoint) {
387
+ const withLeadingSlash = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
388
+ if (/^\/api\/v1(\/|$)/i.test(withLeadingSlash)) {
389
+ return withLeadingSlash.replace(/^\/api/i, "");
390
+ }
391
+ return withLeadingSlash;
392
+ }
57
393
  var WhisperContext = class _WhisperContext {
58
394
  apiKey;
59
395
  baseUrl;
60
396
  defaultProject;
61
- orgId;
62
397
  timeoutMs;
63
398
  retryConfig;
64
399
  projectRefToId = /* @__PURE__ */ new Map();
@@ -72,9 +407,8 @@ var WhisperContext = class _WhisperContext {
72
407
  });
73
408
  }
74
409
  this.apiKey = config.apiKey;
75
- this.baseUrl = config.baseUrl || "https://context.usewhisper.dev";
410
+ this.baseUrl = normalizeBaseUrl(config.baseUrl || "https://context.usewhisper.dev");
76
411
  this.defaultProject = config.project;
77
- this.orgId = config.orgId;
78
412
  this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
79
413
  this.retryConfig = {
80
414
  maxAttempts: config.retry?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS,
@@ -87,7 +421,6 @@ var WhisperContext = class _WhisperContext {
87
421
  apiKey: this.apiKey,
88
422
  baseUrl: this.baseUrl,
89
423
  project,
90
- orgId: this.orgId,
91
424
  timeoutMs: this.timeoutMs,
92
425
  retry: this.retryConfig
93
426
  });
@@ -204,20 +537,28 @@ var WhisperContext = class _WhisperContext {
204
537
  }
205
538
  async request(endpoint, options = {}) {
206
539
  const maxAttempts = Math.max(1, this.retryConfig.maxAttempts);
540
+ const normalizedEndpoint = normalizeEndpoint(endpoint);
207
541
  let lastError;
208
542
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
209
543
  const controller = new AbortController();
210
544
  const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
211
545
  try {
212
- const response = await fetch(`${this.baseUrl}${endpoint}`, {
546
+ const headers = {
547
+ "Content-Type": "application/json",
548
+ ...options.headers
549
+ };
550
+ const hasAuthHeader = Object.keys(headers).some((k) => k.toLowerCase() === "authorization");
551
+ const hasApiKeyHeader = Object.keys(headers).some((k) => k.toLowerCase() === "x-api-key");
552
+ if (!hasAuthHeader) {
553
+ headers.Authorization = `Bearer ${this.apiKey}`;
554
+ }
555
+ if (!hasApiKeyHeader) {
556
+ headers["X-API-Key"] = this.apiKey;
557
+ }
558
+ const response = await fetch(`${this.baseUrl}${normalizedEndpoint}`, {
213
559
  ...options,
214
560
  signal: controller.signal,
215
- headers: {
216
- Authorization: `Bearer ${this.apiKey}`,
217
- "Content-Type": "application/json",
218
- ...this.orgId ? { "X-Whisper-Org-Id": this.orgId } : {},
219
- ...options.headers
220
- }
561
+ headers
221
562
  });
222
563
  clearTimeout(timeout);
223
564
  if (!response.ok) {
@@ -406,6 +747,34 @@ var WhisperContext = class _WhisperContext {
406
747
  return { id, success: true, path: "legacy", fallback_used: true };
407
748
  });
408
749
  }
750
+ async addMemoriesBulk(params) {
751
+ const projectRef = this.getRequiredProject(params.project);
752
+ return this.withProjectRefFallback(projectRef, (project) => this.request("/v1/memory/bulk", {
753
+ method: "POST",
754
+ body: JSON.stringify({ ...params, project })
755
+ }));
756
+ }
757
+ async extractMemories(params) {
758
+ const projectRef = this.getRequiredProject(params.project);
759
+ return this.withProjectRefFallback(projectRef, (project) => this.request("/v1/memory/extract", {
760
+ method: "POST",
761
+ body: JSON.stringify({ ...params, project })
762
+ }));
763
+ }
764
+ async extractSessionMemories(params) {
765
+ const projectRef = this.getRequiredProject(params.project);
766
+ return this.withProjectRefFallback(projectRef, (project) => this.request("/v1/memory/extract/session", {
767
+ method: "POST",
768
+ body: JSON.stringify({
769
+ ...params,
770
+ project,
771
+ messages: params.messages.map((m) => ({
772
+ ...m,
773
+ timestamp: m.timestamp || (/* @__PURE__ */ new Date()).toISOString()
774
+ }))
775
+ })
776
+ }));
777
+ }
409
778
  async searchMemories(params) {
410
779
  const projectRef = this.getRequiredProject(params.project);
411
780
  return this.withProjectRefFallback(projectRef, (project) => this.request("/v1/memory/search", {
@@ -602,6 +971,9 @@ var WhisperContext = class _WhisperContext {
602
971
  };
603
972
  memory = {
604
973
  add: (params) => this.addMemory(params),
974
+ addBulk: (params) => this.addMemoriesBulk(params),
975
+ extract: (params) => this.extractMemories(params),
976
+ extractSession: (params) => this.extractSessionMemories(params),
605
977
  search: (params) => this.searchMemories(params),
606
978
  searchSOTA: (params) => this.searchMemoriesSOTA(params),
607
979
  ingestSession: (params) => this.ingestSession(params),
@@ -640,6 +1012,10 @@ var WhisperContext = class _WhisperContext {
640
1012
  var index_default = WhisperContext;
641
1013
  // Annotate the CommonJS export names for ESM import in node:
642
1014
  0 && (module.exports = {
1015
+ Whisper,
1016
+ WhisperAgentMiddleware,
643
1017
  WhisperContext,
644
- WhisperError
1018
+ WhisperDefault,
1019
+ WhisperError,
1020
+ createAgentMiddleware
645
1021
  });