@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.mjs CHANGED
@@ -1,3 +1,319 @@
1
+ // ../src/sdk/whisper-agent.ts
2
+ var Whisper = class {
3
+ client;
4
+ options;
5
+ sessionId;
6
+ userId;
7
+ constructor(options) {
8
+ if (!options.apiKey) {
9
+ throw new Error("API key is required");
10
+ }
11
+ const clientConfig = {
12
+ apiKey: options.apiKey,
13
+ baseUrl: options.baseUrl,
14
+ project: options.project || "default"
15
+ };
16
+ if (options.timeoutMs) clientConfig.timeoutMs = options.timeoutMs;
17
+ if (options.retry) clientConfig.retry = options.retry;
18
+ this.client = new WhisperContext(clientConfig);
19
+ const finalRetry = options.retry || { maxAttempts: 3, baseDelayMs: 250, maxDelayMs: 2e3 };
20
+ this.options = {
21
+ apiKey: options.apiKey,
22
+ baseUrl: options.baseUrl || "https://context.usewhisper.dev",
23
+ project: options.project || "default",
24
+ timeoutMs: options.timeoutMs || 15e3,
25
+ retry: finalRetry,
26
+ contextLimit: options.contextLimit ?? 10,
27
+ memoryTypes: options.memoryTypes ?? ["factual", "preference", "event", "goal", "relationship", "opinion", "instruction"],
28
+ contextPrefix: options.contextPrefix ?? "Relevant context:",
29
+ autoExtract: options.autoExtract ?? true,
30
+ autoExtractMinConfidence: options.autoExtractMinConfidence ?? 0.65,
31
+ maxMemoriesPerCapture: options.maxMemoriesPerCapture ?? 5
32
+ };
33
+ }
34
+ /**
35
+ * Set session ID for conversation tracking
36
+ */
37
+ session(sessionId) {
38
+ this.sessionId = sessionId;
39
+ return this;
40
+ }
41
+ /**
42
+ * Set user ID for user-specific memories
43
+ */
44
+ user(userId) {
45
+ this.userId = userId;
46
+ return this;
47
+ }
48
+ /**
49
+ * Get relevant context BEFORE your LLM call
50
+ *
51
+ * @param query - What you want to know / user question
52
+ * @returns Context string and raw results
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * const { context, results, count } = await whisper.getContext(
57
+ * "What are user's preferences?",
58
+ * { userId: "user-123" }
59
+ * );
60
+ *
61
+ * // Results: [
62
+ * // { content: "User prefers dark mode", type: "preference", score: 0.95 },
63
+ * // { content: "Allergic to nuts", type: "factual", score: 0.89 }
64
+ * // ]
65
+ * ```
66
+ */
67
+ async getContext(query, options) {
68
+ const result = await this.client.query({
69
+ project: options?.project ?? this.options.project,
70
+ query,
71
+ top_k: options?.limit ?? this.options.contextLimit,
72
+ include_memories: true,
73
+ user_id: options?.userId ?? this.userId,
74
+ session_id: options?.sessionId ?? this.sessionId
75
+ });
76
+ const context = result.results.map((r, i) => `[${i + 1}] ${r.content}`).join("\n");
77
+ return {
78
+ context: context ? `${this.options.contextPrefix}
79
+ ${context}` : "",
80
+ results: result.results,
81
+ count: result.meta.total
82
+ };
83
+ }
84
+ /**
85
+ * Remember what happened AFTER your LLM response
86
+ *
87
+ * Fire-and-forget - doesn't block your response
88
+ *
89
+ * @param content - What your LLM responded with
90
+ * @returns Promise that resolves when stored (or fails silently)
91
+ *
92
+ * @example
93
+ * ```typescript
94
+ * const llmResponse = "I've set your theme to dark mode and removed nuts from recommendations.";
95
+ *
96
+ * await whisper.remember(llmResponse, { userId: "user-123" });
97
+ * // → Auto-extracts: "theme set to dark mode", "nut allergy"
98
+ * // → Stored as preferences
99
+ * ```
100
+ */
101
+ async remember(content, options) {
102
+ if (!content || content.length < 5) {
103
+ return { success: false };
104
+ }
105
+ try {
106
+ if (this.options.autoExtract) {
107
+ const extraction = await this.client.extractMemories({
108
+ project: options?.project ?? this.options.project,
109
+ message: content,
110
+ user_id: options?.userId ?? this.userId,
111
+ session_id: options?.sessionId ?? this.sessionId,
112
+ enable_pattern: true,
113
+ enable_inference: true,
114
+ min_confidence: this.options.autoExtractMinConfidence
115
+ });
116
+ const extractedMemories = (extraction.all || []).filter((m) => (m.confidence || 0) >= this.options.autoExtractMinConfidence).slice(0, this.options.maxMemoriesPerCapture);
117
+ if (extractedMemories.length > 0) {
118
+ const bulk = await this.client.addMemoriesBulk({
119
+ project: options?.project ?? this.options.project,
120
+ async: false,
121
+ memories: extractedMemories.map((m) => ({
122
+ content: m.content,
123
+ memory_type: m.memoryType,
124
+ user_id: options?.userId ?? this.userId,
125
+ session_id: options?.sessionId ?? this.sessionId,
126
+ importance: Math.max(0.5, Math.min(1, m.confidence || 0.7)),
127
+ confidence: m.confidence || 0.7,
128
+ entity_mentions: m.entityMentions || [],
129
+ event_date: m.eventDate || void 0,
130
+ metadata: {
131
+ extracted: true,
132
+ extraction_method: extraction.extractionMethod,
133
+ extraction_reasoning: m.reasoning,
134
+ inferred: Boolean(m.inferred)
135
+ }
136
+ }))
137
+ });
138
+ const memoryIds = this.extractMemoryIdsFromBulkResponse(bulk);
139
+ return {
140
+ success: true,
141
+ memoryId: memoryIds[0],
142
+ memoryIds: memoryIds.length > 0 ? memoryIds : void 0,
143
+ extracted: extractedMemories.length
144
+ };
145
+ }
146
+ }
147
+ const result = await this.client.addMemory({
148
+ project: options?.project ?? this.options.project,
149
+ content,
150
+ user_id: options?.userId ?? this.userId,
151
+ session_id: options?.sessionId ?? this.sessionId
152
+ });
153
+ return {
154
+ success: true,
155
+ memoryId: result?.id
156
+ };
157
+ } catch (error) {
158
+ console.error("[Whisper] Remember failed:", error);
159
+ return { success: false };
160
+ }
161
+ }
162
+ /**
163
+ * Alias for remember() - same thing
164
+ */
165
+ async capture(content, options) {
166
+ return this.remember(content, options);
167
+ }
168
+ /**
169
+ * Capture from multiple messages (e.g., full conversation)
170
+ */
171
+ async captureSession(messages, options) {
172
+ try {
173
+ const result = await this.client.ingestSession({
174
+ project: options?.project ?? this.options.project,
175
+ session_id: options?.sessionId ?? this.sessionId ?? "default",
176
+ user_id: options?.userId ?? this.userId,
177
+ messages: messages.filter((m) => m.role !== "system").map((m) => ({
178
+ role: m.role,
179
+ content: m.content,
180
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
181
+ }))
182
+ });
183
+ return {
184
+ success: true,
185
+ extracted: result?.memories_created ?? 0
186
+ };
187
+ } catch (error) {
188
+ console.error("[Whisper] Session capture failed:", error);
189
+ return { success: false, extracted: 0 };
190
+ }
191
+ }
192
+ /**
193
+ * Run a full agent turn with automatic memory read (before) + write (after).
194
+ */
195
+ async runTurn(params) {
196
+ const contextResult = await this.getContext(params.userMessage, {
197
+ userId: params.userId,
198
+ sessionId: params.sessionId,
199
+ project: params.project,
200
+ limit: params.limit
201
+ });
202
+ const prompt = contextResult.context ? `${contextResult.context}
203
+
204
+ User: ${params.userMessage}` : params.userMessage;
205
+ const response = await params.generate(prompt);
206
+ const captureResult = await this.captureSession(
207
+ [
208
+ { role: "user", content: params.userMessage },
209
+ { role: "assistant", content: response }
210
+ ],
211
+ {
212
+ userId: params.userId,
213
+ sessionId: params.sessionId,
214
+ project: params.project
215
+ }
216
+ );
217
+ return {
218
+ response,
219
+ context: contextResult.context,
220
+ count: contextResult.count,
221
+ extracted: captureResult.extracted
222
+ };
223
+ }
224
+ /**
225
+ * Direct access to WhisperContext for advanced usage
226
+ */
227
+ raw() {
228
+ return this.client;
229
+ }
230
+ extractMemoryIdsFromBulkResponse(bulkResponse) {
231
+ const ids = [];
232
+ if (Array.isArray(bulkResponse?.memories)) {
233
+ for (const memory of bulkResponse.memories) {
234
+ if (memory?.id) ids.push(memory.id);
235
+ }
236
+ }
237
+ if (bulkResponse?.memory?.id) {
238
+ ids.push(bulkResponse.memory.id);
239
+ }
240
+ if (bulkResponse?.id) {
241
+ ids.push(bulkResponse.id);
242
+ }
243
+ return Array.from(new Set(ids));
244
+ }
245
+ };
246
+ var whisper_agent_default = Whisper;
247
+
248
+ // ../src/sdk/middleware.ts
249
+ var WhisperAgentMiddleware = class {
250
+ whisper;
251
+ promptBuilder;
252
+ constructor(config) {
253
+ this.whisper = new Whisper(config);
254
+ this.promptBuilder = config.promptBuilder || (({ context, userMessage }) => {
255
+ if (!context) return userMessage;
256
+ return `${context}
257
+
258
+ User: ${userMessage}`;
259
+ });
260
+ }
261
+ async beforeTurn(params) {
262
+ const contextResult = await this.whisper.getContext(params.userMessage, {
263
+ userId: params.userId,
264
+ sessionId: params.sessionId,
265
+ project: params.project,
266
+ limit: params.contextLimit
267
+ });
268
+ const prompt = this.promptBuilder({
269
+ context: contextResult.context,
270
+ userMessage: params.userMessage
271
+ });
272
+ return {
273
+ prompt,
274
+ context: contextResult.context,
275
+ contextCount: contextResult.count
276
+ };
277
+ }
278
+ async afterTurn(params) {
279
+ return this.whisper.captureSession(
280
+ [
281
+ { role: "user", content: params.userMessage },
282
+ { role: "assistant", content: params.assistantMessage }
283
+ ],
284
+ {
285
+ userId: params.userId,
286
+ sessionId: params.sessionId,
287
+ project: params.project
288
+ }
289
+ );
290
+ }
291
+ async wrapGenerate(params) {
292
+ const before = await this.beforeTurn(params);
293
+ const response = await params.generate(before.prompt);
294
+ const after = await this.afterTurn({
295
+ userMessage: params.userMessage,
296
+ assistantMessage: response,
297
+ userId: params.userId,
298
+ sessionId: params.sessionId,
299
+ project: params.project
300
+ });
301
+ return {
302
+ response,
303
+ prompt: before.prompt,
304
+ context: before.context,
305
+ contextCount: before.contextCount,
306
+ extracted: after.extracted
307
+ };
308
+ }
309
+ raw() {
310
+ return this.whisper;
311
+ }
312
+ };
313
+ function createAgentMiddleware(config) {
314
+ return new WhisperAgentMiddleware(config);
315
+ }
316
+
1
317
  // ../src/sdk/index.ts
2
318
  var WhisperError = class extends Error {
3
319
  code;
@@ -28,11 +344,24 @@ function getBackoffDelay(attempt, base, max) {
28
344
  function isLikelyProjectId(projectRef) {
29
345
  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);
30
346
  }
347
+ function normalizeBaseUrl(url) {
348
+ let normalized = url.trim().replace(/\/+$/, "");
349
+ normalized = normalized.replace(/\/api\/v1$/i, "");
350
+ normalized = normalized.replace(/\/v1$/i, "");
351
+ normalized = normalized.replace(/\/api$/i, "");
352
+ return normalized;
353
+ }
354
+ function normalizeEndpoint(endpoint) {
355
+ const withLeadingSlash = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
356
+ if (/^\/api\/v1(\/|$)/i.test(withLeadingSlash)) {
357
+ return withLeadingSlash.replace(/^\/api/i, "");
358
+ }
359
+ return withLeadingSlash;
360
+ }
31
361
  var WhisperContext = class _WhisperContext {
32
362
  apiKey;
33
363
  baseUrl;
34
364
  defaultProject;
35
- orgId;
36
365
  timeoutMs;
37
366
  retryConfig;
38
367
  projectRefToId = /* @__PURE__ */ new Map();
@@ -46,9 +375,8 @@ var WhisperContext = class _WhisperContext {
46
375
  });
47
376
  }
48
377
  this.apiKey = config.apiKey;
49
- this.baseUrl = config.baseUrl || "https://context.usewhisper.dev";
378
+ this.baseUrl = normalizeBaseUrl(config.baseUrl || "https://context.usewhisper.dev");
50
379
  this.defaultProject = config.project;
51
- this.orgId = config.orgId;
52
380
  this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
53
381
  this.retryConfig = {
54
382
  maxAttempts: config.retry?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS,
@@ -61,7 +389,6 @@ var WhisperContext = class _WhisperContext {
61
389
  apiKey: this.apiKey,
62
390
  baseUrl: this.baseUrl,
63
391
  project,
64
- orgId: this.orgId,
65
392
  timeoutMs: this.timeoutMs,
66
393
  retry: this.retryConfig
67
394
  });
@@ -178,20 +505,28 @@ var WhisperContext = class _WhisperContext {
178
505
  }
179
506
  async request(endpoint, options = {}) {
180
507
  const maxAttempts = Math.max(1, this.retryConfig.maxAttempts);
508
+ const normalizedEndpoint = normalizeEndpoint(endpoint);
181
509
  let lastError;
182
510
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
183
511
  const controller = new AbortController();
184
512
  const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
185
513
  try {
186
- const response = await fetch(`${this.baseUrl}${endpoint}`, {
514
+ const headers = {
515
+ "Content-Type": "application/json",
516
+ ...options.headers
517
+ };
518
+ const hasAuthHeader = Object.keys(headers).some((k) => k.toLowerCase() === "authorization");
519
+ const hasApiKeyHeader = Object.keys(headers).some((k) => k.toLowerCase() === "x-api-key");
520
+ if (!hasAuthHeader) {
521
+ headers.Authorization = `Bearer ${this.apiKey}`;
522
+ }
523
+ if (!hasApiKeyHeader) {
524
+ headers["X-API-Key"] = this.apiKey;
525
+ }
526
+ const response = await fetch(`${this.baseUrl}${normalizedEndpoint}`, {
187
527
  ...options,
188
528
  signal: controller.signal,
189
- headers: {
190
- Authorization: `Bearer ${this.apiKey}`,
191
- "Content-Type": "application/json",
192
- ...this.orgId ? { "X-Whisper-Org-Id": this.orgId } : {},
193
- ...options.headers
194
- }
529
+ headers
195
530
  });
196
531
  clearTimeout(timeout);
197
532
  if (!response.ok) {
@@ -380,6 +715,34 @@ var WhisperContext = class _WhisperContext {
380
715
  return { id, success: true, path: "legacy", fallback_used: true };
381
716
  });
382
717
  }
718
+ async addMemoriesBulk(params) {
719
+ const projectRef = this.getRequiredProject(params.project);
720
+ return this.withProjectRefFallback(projectRef, (project) => this.request("/v1/memory/bulk", {
721
+ method: "POST",
722
+ body: JSON.stringify({ ...params, project })
723
+ }));
724
+ }
725
+ async extractMemories(params) {
726
+ const projectRef = this.getRequiredProject(params.project);
727
+ return this.withProjectRefFallback(projectRef, (project) => this.request("/v1/memory/extract", {
728
+ method: "POST",
729
+ body: JSON.stringify({ ...params, project })
730
+ }));
731
+ }
732
+ async extractSessionMemories(params) {
733
+ const projectRef = this.getRequiredProject(params.project);
734
+ return this.withProjectRefFallback(projectRef, (project) => this.request("/v1/memory/extract/session", {
735
+ method: "POST",
736
+ body: JSON.stringify({
737
+ ...params,
738
+ project,
739
+ messages: params.messages.map((m) => ({
740
+ ...m,
741
+ timestamp: m.timestamp || (/* @__PURE__ */ new Date()).toISOString()
742
+ }))
743
+ })
744
+ }));
745
+ }
383
746
  async searchMemories(params) {
384
747
  const projectRef = this.getRequiredProject(params.project);
385
748
  return this.withProjectRefFallback(projectRef, (project) => this.request("/v1/memory/search", {
@@ -576,6 +939,9 @@ var WhisperContext = class _WhisperContext {
576
939
  };
577
940
  memory = {
578
941
  add: (params) => this.addMemory(params),
942
+ addBulk: (params) => this.addMemoriesBulk(params),
943
+ extract: (params) => this.extractMemories(params),
944
+ extractSession: (params) => this.extractSessionMemories(params),
579
945
  search: (params) => this.searchMemories(params),
580
946
  searchSOTA: (params) => this.searchMemoriesSOTA(params),
581
947
  ingestSession: (params) => this.ingestSession(params),
@@ -613,7 +979,11 @@ var WhisperContext = class _WhisperContext {
613
979
  };
614
980
  var index_default = WhisperContext;
615
981
  export {
982
+ Whisper,
983
+ WhisperAgentMiddleware,
616
984
  WhisperContext,
985
+ whisper_agent_default as WhisperDefault,
617
986
  WhisperError,
987
+ createAgentMiddleware,
618
988
  index_default as default
619
989
  };
package/package.json CHANGED
@@ -1,56 +1,56 @@
1
- {
2
- "name": "@usewhisper/sdk",
3
- "version": "1.1.0",
4
- "scripts": {
5
- "build": "tsup ../src/sdk/index.ts --format esm,cjs --dts --out-dir .",
6
- "prepublishOnly": "npm run build"
7
- },
8
- "description": "TypeScript SDK for Whisper Context API - Add reliable context to your AI agents",
9
- "main": "index.js",
10
- "module": "index.mjs",
11
- "types": "index.d.ts",
12
- "exports": {
13
- ".": {
14
- "types": "./index.d.ts",
15
- "import": "./index.mjs",
16
- "require": "./index.js"
17
- }
18
- },
19
- "files": [
20
- "index.js",
21
- "index.mjs",
22
- "index.d.ts",
23
- "index.d.mts",
24
- "README.md"
25
- ],
26
- "keywords": [
27
- "whisper",
28
- "context",
29
- "ai",
30
- "llm",
31
- "rag",
32
- "embeddings",
33
- "vector-search",
34
- "knowledge-graph",
35
- "semantic-search"
36
- ],
37
- "author": "Whisper",
38
- "license": "MIT",
39
- "repository": {
40
- "type": "git",
41
- "url": "https://github.com/Alixus/"
42
- },
43
- "homepage": "https://usewhisper.dev",
44
- "bugs": {
45
- "url": "https://github.com/Alinxus/whisper/issues"
46
- },
47
- "devDependencies": {
48
- "@types/node": "^22.0.0",
49
- "tsup": "^8.3.0",
50
- "typescript": "^5.7.0"
51
- },
52
- "peerDependencies": {},
53
- "engines": {
54
- "node": ">=18.0.0"
55
- }
56
- }
1
+ {
2
+ "name": "@usewhisper/sdk",
3
+ "version": "2.1.0",
4
+ "scripts": {
5
+ "build": "tsup ../src/sdk/index.ts --format esm,cjs --dts --out-dir .",
6
+ "prepublishOnly": "npm run build"
7
+ },
8
+ "description": "TypeScript SDK for Whisper Context API - Add reliable context to your AI agents",
9
+ "main": "index.js",
10
+ "module": "index.mjs",
11
+ "types": "index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./index.d.ts",
15
+ "import": "./index.mjs",
16
+ "require": "./index.js"
17
+ }
18
+ },
19
+ "files": [
20
+ "index.js",
21
+ "index.mjs",
22
+ "index.d.ts",
23
+ "index.d.mts",
24
+ "README.md"
25
+ ],
26
+ "keywords": [
27
+ "whisper",
28
+ "context",
29
+ "ai",
30
+ "llm",
31
+ "rag",
32
+ "embeddings",
33
+ "vector-search",
34
+ "knowledge-graph",
35
+ "semantic-search"
36
+ ],
37
+ "author": "Whisper",
38
+ "license": "MIT",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/Alixus/"
42
+ },
43
+ "homepage": "https://usewhisper.dev",
44
+ "bugs": {
45
+ "url": "https://github.com/Alinxus/whisper/issues"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^22.0.0",
49
+ "tsup": "^8.3.0",
50
+ "typescript": "^5.7.0"
51
+ },
52
+ "peerDependencies": {},
53
+ "engines": {
54
+ "node": ">=18.0.0"
55
+ }
56
+ }