@horribleprogram/sdk 0.1.4 → 0.1.6

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 CHANGED
@@ -3,14 +3,18 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@horribleprogram/sdk.svg)](https://www.npmjs.com/package/@horribleprogram/sdk)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- Official JavaScript/TypeScript SDK for the **Botcierge Intent Classification API**.
6
+ Official JavaScript/TypeScript SDK for **Botcierge Intent Classification**.
7
+
8
+ Starting with `v0.1.5`, the SDK runs **completely locally** on your machine. It loads the `sentence-transformers/all-MiniLM-L12-v2` model using ONNX runtime and performs classification via cosine similarity against a built-in static intents taxonomy.
9
+
10
+ No API keys, configuration, or active internet connection (after first run) are required.
7
11
 
8
12
  ## Features
9
13
 
10
- - **TypeScript Native**: Full type definitions for all API responses.
11
- - **Lightweight**: Zero dependencies (uses native `fetch`).
12
- - **Flexible**: Easy to use for both simple scripts and complex applications.
13
- - **Cross-platform**: Works in Node.js, Browsers, and Edge environments.
14
+ - **Purely Local**: High-speed offline intent classification with zero external API calls or latency.
15
+ - **ONNX-Powered**: Powered by `@xenova/transformers` for running `all-MiniLM-L12-v2` locally.
16
+ - **TypeScript Native**: Full type safety for class instances, options, and classification outputs.
17
+ - **Automatic Caching**: Model weights (approx. 120MB) are downloaded automatically on the first run and cached locally.
14
18
 
15
19
  ## Installation
16
20
 
@@ -23,6 +27,7 @@ npm install @horribleprogram/sdk
23
27
  ```typescript
24
28
  import { query_intent } from '@horribleprogram/sdk';
25
29
 
30
+ // On the first run, the local model will download and cache.
26
31
  const result = await query_intent("I'd like to share some food");
27
32
 
28
33
  console.log(result.domain); // FOODLINK
@@ -30,46 +35,43 @@ console.log(result.intent); // share_food
30
35
  console.log(result.confidence); // high
31
36
  ```
32
37
 
33
- No configuration needed — the SDK points to the hosted API at `https://horribleprogram-intentapi.hf.space` by default.
38
+ ## Usage
34
39
 
35
- ## Try it in Node
40
+ ### Simple Usage
41
+ The simplest way to use the SDK is through the default helper function:
36
42
 
37
- Create a file `demo.mjs` and run it with `node demo.mjs`:
43
+ ```javascript
44
+ import { query_intent } from '@horribleprogram/sdk';
38
45
 
39
- ```js
40
- import { Botcierge, query_intent } from '@horribleprogram/sdk'
46
+ const result = await query_intent("I'm hungry");
47
+ console.log(result);
48
+ // { domain: 'FOODLINK', intent: 'request_food', confidence: 'high', scores: { request_food: 0.975 } }
49
+ ```
41
50
 
42
- // Zero-config — hits the hosted API by default
43
- console.log(await query_intent("I'm hungry"))
44
- console.log(await query_intent("I need help with a bill"))
51
+ ### Dedicated Instance
52
+ For class-based architectures, instantiate the `Botcierge` class:
45
53
 
46
- // Or with an explicit client
47
- const client = new Botcierge()
48
- console.log(await client.query_intent("I want to add milk to my list"))
49
- ```
54
+ ```javascript
55
+ import { Botcierge } from '@horribleprogram/sdk';
50
56
 
51
- Example output:
52
- ```json
53
- { "domain": "FOODLINK", "intent": "request_food", "confidence": "high" }
54
- { "domain": "BILLBRIDGE", "intent": "request_bill_help", "confidence": "high" }
55
- { "domain": "SHOP_SAVVY", "intent": "add_item", "confidence": "high" }
57
+ const client = new Botcierge();
58
+ const result = await client.query_intent("I want to add milk to my list");
59
+ console.log(result);
60
+ // { domain: 'SHOP_SAVVY', intent: 'add_item', confidence: 'high', scores: { add_item: 0.884 } }
56
61
  ```
57
62
 
58
63
  ## API Reference
59
64
 
60
65
  ### `query_intent(utterance: string): Promise<IntentResult>`
61
-
62
- Classifies a string using the default client (hosted API).
66
+ Classifies a string using the default local instance.
63
67
 
64
68
  ### `class Botcierge`
65
-
66
69
  #### `constructor(config?: BotciergeConfig)`
67
-
68
- - `config.baseUrl`: Override the API base URL (default: `https://horribleprogram-intentapi.hf.space`).
70
+ Creates a new intent classification instance.
71
+ - `config`: Currently empty (reserved for future options).
69
72
 
70
73
  #### `query_intent(utterance: string): Promise<IntentResult>`
71
-
72
- Classifies a string into a specific domain and intent.
74
+ Classifies a string locally into a specific domain and intent.
73
75
 
74
76
  ### Types
75
77
 
@@ -83,27 +85,23 @@ interface IntentResult {
83
85
  }
84
86
  ```
85
87
 
86
- ## Error Handling
87
-
88
- ```typescript
89
- import { Botcierge } from '@horribleprogram/sdk';
90
-
91
- const client = new Botcierge({ baseUrl: 'https://my-own-instance.com' });
92
-
93
- try {
94
- await client.query_intent("hello");
95
- } catch (e) {
96
- console.log(e.message); // "Botcierge API error: ..."
97
- }
98
- ```
99
-
100
88
  ## Development
101
89
 
90
+ To build and test the SDK locally:
91
+
102
92
  ```bash
93
+ # Clone the repository and install dependencies
94
+ cd sdk-js
95
+ npm install
96
+
97
+ # Run unit tests
103
98
  npm test
99
+
100
+ # Build files (CJS + ESM + DTS)
104
101
  npm run build
105
102
  ```
106
103
 
107
104
  ## License
108
105
 
109
106
  MIT © [horribleprogram](https://npmjs.com/~horribleprogram)
107
+
@@ -199,74 +199,44 @@ function cosineSimilarity(v1, v2) {
199
199
  return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
200
200
  }
201
201
  var Botcierge = class {
202
- baseUrl;
203
- apiKey;
204
202
  constructor(config = {}) {
205
- this.baseUrl = config.baseUrl || "https://horribleprogram-intentapi.hf.space";
206
- this.apiKey = config.apiKey;
207
- }
208
- getApiKey() {
209
- if (this.apiKey) return this.apiKey;
210
- if (typeof process !== "undefined" && process.env) {
211
- return process.env.BOTCIERGE_API_KEY || process.env.OPENAI_API_KEY;
212
- }
213
- return void 0;
214
203
  }
215
204
  async query_intent(utterance) {
216
- const apiKey = this.getApiKey();
217
- if (apiKey) {
218
- const headers = {
219
- "Content-Type": "application/json",
220
- "Authorization": `Bearer ${apiKey}`,
221
- "X-API-Key": apiKey
222
- };
223
- const response = await fetch(`${this.baseUrl}/classify`, {
224
- method: "POST",
225
- headers,
226
- body: JSON.stringify({ utterance })
227
- });
228
- if (!response.ok) {
229
- const error = await response.json().catch(() => ({ detail: "Unknown error" }));
230
- throw new Error(`Botcierge API error: ${error.detail || response.statusText}`);
231
- }
232
- return await response.json();
233
- } else {
234
- const extractor = await getExtractor();
235
- const output = await extractor(utterance, { pooling: "mean", normalize: true });
236
- const queryVector = Array.from(output.data);
237
- const taxEmbeddings = await getTaxonomyEmbeddings();
238
- let bestSim = -1;
239
- let bestItem = null;
240
- for (const item of taxEmbeddings) {
241
- const sim = cosineSimilarity(queryVector, item.vector);
242
- if (sim > bestSim) {
243
- bestSim = sim;
244
- bestItem = item;
245
- }
246
- }
247
- if (!bestItem || bestSim < 0.35) {
248
- return {
249
- domain: "unknown",
250
- intent: "unknown",
251
- confidence: "low",
252
- scores: {}
253
- };
254
- }
255
- let confidence = "low";
256
- if (bestSim >= 0.7) {
257
- confidence = "high";
258
- } else if (bestSim >= 0.5) {
259
- confidence = "medium";
205
+ const extractor = await getExtractor();
206
+ const output = await extractor(utterance, { pooling: "mean", normalize: true });
207
+ const queryVector = Array.from(output.data);
208
+ const taxEmbeddings = await getTaxonomyEmbeddings();
209
+ let bestSim = -1;
210
+ let bestItem = null;
211
+ for (const item of taxEmbeddings) {
212
+ const sim = cosineSimilarity(queryVector, item.vector);
213
+ if (sim > bestSim) {
214
+ bestSim = sim;
215
+ bestItem = item;
260
216
  }
217
+ }
218
+ if (!bestItem || bestSim < 0.35) {
261
219
  return {
262
- domain: bestItem.domain,
263
- intent: bestItem.intent,
264
- confidence,
265
- scores: {
266
- [bestItem.intent]: bestSim
267
- }
220
+ domain: "unknown",
221
+ intent: "unknown",
222
+ confidence: "low",
223
+ scores: {}
268
224
  };
269
225
  }
226
+ let confidence = "low";
227
+ if (bestSim >= 0.7) {
228
+ confidence = "high";
229
+ } else if (bestSim >= 0.5) {
230
+ confidence = "medium";
231
+ }
232
+ return {
233
+ domain: bestItem.domain,
234
+ intent: bestItem.intent,
235
+ confidence,
236
+ scores: {
237
+ [bestItem.intent]: bestSim
238
+ }
239
+ };
270
240
  }
271
241
  };
272
242
  var defaultClient = new Botcierge();
package/dist/index.cjs CHANGED
@@ -224,74 +224,44 @@ function cosineSimilarity(v1, v2) {
224
224
  return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
225
225
  }
226
226
  var Botcierge = class {
227
- baseUrl;
228
- apiKey;
229
227
  constructor(config = {}) {
230
- this.baseUrl = config.baseUrl || "https://horribleprogram-intentapi.hf.space";
231
- this.apiKey = config.apiKey;
232
- }
233
- getApiKey() {
234
- if (this.apiKey) return this.apiKey;
235
- if (typeof process !== "undefined" && process.env) {
236
- return process.env.BOTCIERGE_API_KEY || process.env.OPENAI_API_KEY;
237
- }
238
- return void 0;
239
228
  }
240
229
  async query_intent(utterance) {
241
- const apiKey = this.getApiKey();
242
- if (apiKey) {
243
- const headers = {
244
- "Content-Type": "application/json",
245
- "Authorization": `Bearer ${apiKey}`,
246
- "X-API-Key": apiKey
247
- };
248
- const response = await fetch(`${this.baseUrl}/classify`, {
249
- method: "POST",
250
- headers,
251
- body: JSON.stringify({ utterance })
252
- });
253
- if (!response.ok) {
254
- const error = await response.json().catch(() => ({ detail: "Unknown error" }));
255
- throw new Error(`Botcierge API error: ${error.detail || response.statusText}`);
256
- }
257
- return await response.json();
258
- } else {
259
- const extractor = await getExtractor();
260
- const output = await extractor(utterance, { pooling: "mean", normalize: true });
261
- const queryVector = Array.from(output.data);
262
- const taxEmbeddings = await getTaxonomyEmbeddings();
263
- let bestSim = -1;
264
- let bestItem = null;
265
- for (const item of taxEmbeddings) {
266
- const sim = cosineSimilarity(queryVector, item.vector);
267
- if (sim > bestSim) {
268
- bestSim = sim;
269
- bestItem = item;
270
- }
271
- }
272
- if (!bestItem || bestSim < 0.35) {
273
- return {
274
- domain: "unknown",
275
- intent: "unknown",
276
- confidence: "low",
277
- scores: {}
278
- };
279
- }
280
- let confidence = "low";
281
- if (bestSim >= 0.7) {
282
- confidence = "high";
283
- } else if (bestSim >= 0.5) {
284
- confidence = "medium";
230
+ const extractor = await getExtractor();
231
+ const output = await extractor(utterance, { pooling: "mean", normalize: true });
232
+ const queryVector = Array.from(output.data);
233
+ const taxEmbeddings = await getTaxonomyEmbeddings();
234
+ let bestSim = -1;
235
+ let bestItem = null;
236
+ for (const item of taxEmbeddings) {
237
+ const sim = cosineSimilarity(queryVector, item.vector);
238
+ if (sim > bestSim) {
239
+ bestSim = sim;
240
+ bestItem = item;
285
241
  }
242
+ }
243
+ if (!bestItem || bestSim < 0.35) {
286
244
  return {
287
- domain: bestItem.domain,
288
- intent: bestItem.intent,
289
- confidence,
290
- scores: {
291
- [bestItem.intent]: bestSim
292
- }
245
+ domain: "unknown",
246
+ intent: "unknown",
247
+ confidence: "low",
248
+ scores: {}
293
249
  };
294
250
  }
251
+ let confidence = "low";
252
+ if (bestSim >= 0.7) {
253
+ confidence = "high";
254
+ } else if (bestSim >= 0.5) {
255
+ confidence = "medium";
256
+ }
257
+ return {
258
+ domain: bestItem.domain,
259
+ intent: bestItem.intent,
260
+ confidence,
261
+ scores: {
262
+ [bestItem.intent]: bestSim
263
+ }
264
+ };
295
265
  }
296
266
  };
297
267
  var defaultClient = new Botcierge();
package/dist/index.d.cts CHANGED
@@ -5,14 +5,9 @@ interface IntentResult {
5
5
  scores?: Record<string, number>;
6
6
  }
7
7
  interface BotciergeConfig {
8
- baseUrl?: string;
9
- apiKey?: string;
10
8
  }
11
9
  declare class Botcierge {
12
- private baseUrl;
13
- private apiKey?;
14
10
  constructor(config?: BotciergeConfig);
15
- private getApiKey;
16
11
  query_intent(utterance: string): Promise<IntentResult>;
17
12
  }
18
13
  /**
package/dist/index.d.ts CHANGED
@@ -5,14 +5,9 @@ interface IntentResult {
5
5
  scores?: Record<string, number>;
6
6
  }
7
7
  interface BotciergeConfig {
8
- baseUrl?: string;
9
- apiKey?: string;
10
8
  }
11
9
  declare class Botcierge {
12
- private baseUrl;
13
- private apiKey?;
14
10
  constructor(config?: BotciergeConfig);
15
- private getApiKey;
16
11
  query_intent(utterance: string): Promise<IntentResult>;
17
12
  }
18
13
  /**
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Botcierge,
3
3
  query_intent
4
- } from "./chunk-4DZAWXQM.js";
4
+ } from "./chunk-45PYBUYK.js";
5
5
  export {
6
6
  Botcierge,
7
7
  query_intent
package/dist/main.cjs CHANGED
@@ -226,74 +226,44 @@ function cosineSimilarity(v1, v2) {
226
226
  return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
227
227
  }
228
228
  var Botcierge = class {
229
- baseUrl;
230
- apiKey;
231
229
  constructor(config = {}) {
232
- this.baseUrl = config.baseUrl || "https://horribleprogram-intentapi.hf.space";
233
- this.apiKey = config.apiKey;
234
- }
235
- getApiKey() {
236
- if (this.apiKey) return this.apiKey;
237
- if (typeof process !== "undefined" && process.env) {
238
- return process.env.BOTCIERGE_API_KEY || process.env.OPENAI_API_KEY;
239
- }
240
- return void 0;
241
230
  }
242
231
  async query_intent(utterance) {
243
- const apiKey = this.getApiKey();
244
- if (apiKey) {
245
- const headers = {
246
- "Content-Type": "application/json",
247
- "Authorization": `Bearer ${apiKey}`,
248
- "X-API-Key": apiKey
249
- };
250
- const response = await fetch(`${this.baseUrl}/classify`, {
251
- method: "POST",
252
- headers,
253
- body: JSON.stringify({ utterance })
254
- });
255
- if (!response.ok) {
256
- const error = await response.json().catch(() => ({ detail: "Unknown error" }));
257
- throw new Error(`Botcierge API error: ${error.detail || response.statusText}`);
258
- }
259
- return await response.json();
260
- } else {
261
- const extractor = await getExtractor();
262
- const output = await extractor(utterance, { pooling: "mean", normalize: true });
263
- const queryVector = Array.from(output.data);
264
- const taxEmbeddings = await getTaxonomyEmbeddings();
265
- let bestSim = -1;
266
- let bestItem = null;
267
- for (const item of taxEmbeddings) {
268
- const sim = cosineSimilarity(queryVector, item.vector);
269
- if (sim > bestSim) {
270
- bestSim = sim;
271
- bestItem = item;
272
- }
273
- }
274
- if (!bestItem || bestSim < 0.35) {
275
- return {
276
- domain: "unknown",
277
- intent: "unknown",
278
- confidence: "low",
279
- scores: {}
280
- };
281
- }
282
- let confidence = "low";
283
- if (bestSim >= 0.7) {
284
- confidence = "high";
285
- } else if (bestSim >= 0.5) {
286
- confidence = "medium";
232
+ const extractor = await getExtractor();
233
+ const output = await extractor(utterance, { pooling: "mean", normalize: true });
234
+ const queryVector = Array.from(output.data);
235
+ const taxEmbeddings = await getTaxonomyEmbeddings();
236
+ let bestSim = -1;
237
+ let bestItem = null;
238
+ for (const item of taxEmbeddings) {
239
+ const sim = cosineSimilarity(queryVector, item.vector);
240
+ if (sim > bestSim) {
241
+ bestSim = sim;
242
+ bestItem = item;
287
243
  }
244
+ }
245
+ if (!bestItem || bestSim < 0.35) {
288
246
  return {
289
- domain: bestItem.domain,
290
- intent: bestItem.intent,
291
- confidence,
292
- scores: {
293
- [bestItem.intent]: bestSim
294
- }
247
+ domain: "unknown",
248
+ intent: "unknown",
249
+ confidence: "low",
250
+ scores: {}
295
251
  };
296
252
  }
253
+ let confidence = "low";
254
+ if (bestSim >= 0.7) {
255
+ confidence = "high";
256
+ } else if (bestSim >= 0.5) {
257
+ confidence = "medium";
258
+ }
259
+ return {
260
+ domain: bestItem.domain,
261
+ intent: bestItem.intent,
262
+ confidence,
263
+ scores: {
264
+ [bestItem.intent]: bestSim
265
+ }
266
+ };
297
267
  }
298
268
  };
299
269
  var defaultClient = new Botcierge();
package/dist/main.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  query_intent
3
- } from "./chunk-4DZAWXQM.js";
3
+ } from "./chunk-45PYBUYK.js";
4
4
 
5
5
  // src/main.ts
6
6
  import * as readline from "readline";
package/package.json CHANGED
@@ -1,11 +1,22 @@
1
1
  {
2
2
  "name": "@horribleprogram/sdk",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "SDK for Botcierge Intent Classification",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
8
8
  "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ },
15
+ "./dist": {
16
+ "import": "./dist/index.js"
17
+ },
18
+ "./dist/*": "./dist/*"
19
+ },
9
20
  "files": [
10
21
  "dist"
11
22
  ],