@bandito-ai/sdk 0.1.7

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 ADDED
@@ -0,0 +1,162 @@
1
+ # Bandito JavaScript SDK
2
+
3
+ Zero-latency LLM routing via contextual bandits. `pull()` runs Thompson Sampling locally in <1ms via WASM — no network call on the hot path.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pnpm add @bandito-ai/sdk
9
+ ```
10
+
11
+ Requires Node.js 18+. The [Bandito CLI](../../cli/README.md) is a separate binary for account setup, bandit management, and grading:
12
+
13
+ ```bash
14
+ brew install bandito-ai/tap/bandito # or: cargo install --path cli
15
+ bandito signup
16
+ ```
17
+
18
+ ## Quickstart
19
+
20
+ ```typescript
21
+ import { connect, pull, update, close } from "@bandito-ai/sdk";
22
+
23
+ await connect();
24
+
25
+ // Pick the best model+prompt for this query (<1ms, WASM math)
26
+ const result = pull("my-chatbot", { query: userMessage });
27
+
28
+ // Call the winning LLM
29
+ const response = await openai.chat.completions.create({
30
+ model: result.model, // e.g. "gpt-4o"
31
+ messages: [
32
+ { role: "system", content: result.prompt },
33
+ { role: "user", content: userMessage },
34
+ ],
35
+ });
36
+
37
+ // Report what happened
38
+ update(result, {
39
+ queryText: userMessage,
40
+ response: response.choices[0].message.content,
41
+ inputTokens: response.usage.prompt_tokens,
42
+ outputTokens: response.usage.completion_tokens,
43
+ });
44
+
45
+ await close();
46
+ ```
47
+
48
+ Latency is auto-measured between `pull()` and `update()`. Cost is auto-calculated from token counts. Override either by passing `latency` or `cost` explicitly.
49
+
50
+ ## Usage Patterns
51
+
52
+ **Module-level singleton** (simplest):
53
+
54
+ ```typescript
55
+ import { connect, pull, update, close } from "@bandito-ai/sdk";
56
+ await connect({ apiKey: "bnd_..." });
57
+ const result = pull("my-chatbot");
58
+ ```
59
+
60
+ **Explicit client** (recommended for servers):
61
+
62
+ ```typescript
63
+ import { BanditoClient } from "@bandito-ai/sdk";
64
+
65
+ const client = new BanditoClient({ apiKey: "bnd_..." });
66
+ await client.connect();
67
+ const result = client.pull("my-chatbot");
68
+ await client.close();
69
+ ```
70
+
71
+ ## API Reference
72
+
73
+ ### `connect(options?)`
74
+
75
+ Bootstrap: authenticate, fetch bandit state, load WASM engine, flush pending events. **Async.**
76
+
77
+ | Option | Default | Description |
78
+ |--------|---------|-------------|
79
+ | `apiKey` | `BANDITO_API_KEY` env / config file | API key |
80
+ | `baseUrl` | `https://bandito-api.onrender.com` | Cloud API endpoint |
81
+ | `storePath` | `~/.bandito/events.db` | SQLite file for crash-safe event durability |
82
+ | `dataStorage` | `"local"` | `"local"` keeps text on your machine; `"cloud"` sends it to the server |
83
+
84
+ ### `pull(banditName, options?) -> PullResult`
85
+
86
+ Pick the best arm. Pure WASM math, <1ms, no network. **Synchronous.**
87
+
88
+ **Returns** a `PullResult`:
89
+ - `.model` — model name (e.g. `"gpt-4o"`)
90
+ - `.prompt` — system prompt text
91
+ - `.provider` — model provider (e.g. `"openai"`)
92
+ - `.eventId` — UUID linking pull → update → grade
93
+ - `.arm` — full `Arm` object
94
+ - `.scores` — `Map<number, number>` arm scores (for debugging)
95
+
96
+ Pass `exclude: [armId]` to skip a failing arm (circuit breaker pattern).
97
+
98
+ ### `update(pullResult, options?)`
99
+
100
+ Report event data. Writes to local SQLite immediately, flushes to cloud in background. **Synchronous.**
101
+
102
+ | Option | Description |
103
+ |--------|-------------|
104
+ | `queryText` | The user's query |
105
+ | `response` | LLM response (string or object) |
106
+ | `reward` | Immediate reward (0.0–1.0) |
107
+ | `cost` | Cost in dollars (auto-calculated from tokens if omitted) |
108
+ | `latency` | Latency in ms (auto-calculated from pull timing if omitted) |
109
+ | `inputTokens` | Input token count |
110
+ | `outputTokens` | Output token count |
111
+ | `segment` | `Record<string, string>` segment tags |
112
+ | `failed` | Mark as failed LLM call (defaults reward to 0.0) |
113
+
114
+ ### `grade(eventId, grade)`
115
+
116
+ Submit a human grade (0.0–1.0) for a previous event. **Async.** Use the [TUI](../../cli/README.md#tui-grading-workbench) for bulk grading.
117
+
118
+ ### `sync()`
119
+
120
+ Refresh bandit state from cloud. **Async.** Called automatically via background heartbeat.
121
+
122
+ ### `close()`
123
+
124
+ Flush remaining events and shut down background interval. **Async.**
125
+
126
+ ## How It Works
127
+
128
+ The SDK loads the Rust WASM engine during `connect()`. On `pull()`:
129
+
130
+ 1. Sample from the posterior via Thompson Sampling
131
+ 2. Build feature vectors per arm (model + prompt one-hot, query length, latency)
132
+ 3. Score each arm, return the winner
133
+
134
+ All Bayesian updates happen server-side. The SDK refreshes via periodic heartbeat.
135
+
136
+ **Crash safety** — events go to local SQLite (WAL mode) before any network call. Pending events retry on next `connect()`.
137
+
138
+ **Fail-safe** — if the cloud is unreachable, the SDK keeps routing with last-known-good weights. Your app never breaks.
139
+
140
+ ## Configuration
141
+
142
+ Shared `~/.bandito/config.toml` (created by `bandito signup` or `bandito config`):
143
+
144
+ ```toml
145
+ api_key = "bnd_..."
146
+ base_url = "https://bandito-api.onrender.com"
147
+ data_storage = "local"
148
+ ```
149
+
150
+ Environment variables `BANDITO_API_KEY`, `BANDITO_BASE_URL`, `BANDITO_DATA_STORAGE` override the file.
151
+
152
+ ## Development
153
+
154
+ ```bash
155
+ # Build WASM engine
156
+ cd engine && wasm-pack build --target nodejs --out-dir pkg --features wasm
157
+
158
+ # Install and test
159
+ cd sdks/javascript && pnpm install
160
+ pnpm test # 41 tests
161
+ pnpm build # CJS + ESM via tsup
162
+ ```
@@ -0,0 +1,145 @@
1
+ /**
2
+ * SDK types: Arm, PullResult, and internal cache structures.
3
+ */
4
+ /** An arm returned to the user after pull(). Immutable. */
5
+ interface Arm {
6
+ readonly armId: number;
7
+ readonly modelName: string;
8
+ readonly modelProvider: string;
9
+ readonly systemPrompt: string;
10
+ readonly isPromptTemplated: boolean;
11
+ /** Convenience alias for modelName. */
12
+ readonly model: string;
13
+ /** Convenience alias for systemPrompt. */
14
+ readonly prompt: string;
15
+ }
16
+ /** Returned by pull(), passed to update(). Immutable. */
17
+ interface PullResult {
18
+ readonly arm: Arm;
19
+ readonly eventId: string;
20
+ readonly banditId: number;
21
+ readonly banditName: string;
22
+ readonly scores: Readonly<Record<number, number>>;
23
+ /** Convenience reach-through to arm.modelName. */
24
+ readonly model: string;
25
+ /** Convenience reach-through to arm.systemPrompt. */
26
+ readonly prompt: string;
27
+ /** @internal perf timestamp */
28
+ readonly _pullTime: number;
29
+ }
30
+
31
+ /**
32
+ * BanditoClient — main orchestrator for the JS/TS SDK.
33
+ *
34
+ * Mirrors the Python SDK's sync-first design:
35
+ * - pull() is synchronous (WASM math, <1ms)
36
+ * - connect(), grade(), sync(), close() are async (HTTP I/O)
37
+ * - update() is synchronous (SQLite write + fire-and-forget flush)
38
+ */
39
+
40
+ interface ClientOptions {
41
+ apiKey?: string;
42
+ baseUrl?: string;
43
+ storePath?: string;
44
+ dataStorage?: string;
45
+ }
46
+ interface PullOptions {
47
+ query?: string;
48
+ exclude?: number[];
49
+ }
50
+ interface UpdateOptions {
51
+ queryText?: string;
52
+ response?: string | Record<string, unknown>;
53
+ reward?: number;
54
+ cost?: number;
55
+ latency?: number;
56
+ inputTokens?: number;
57
+ outputTokens?: number;
58
+ segment?: Record<string, string>;
59
+ failed?: boolean;
60
+ }
61
+ declare class BanditoClient {
62
+ private apiKey;
63
+ private baseUrl;
64
+ private storePath;
65
+ private dataStorageArg;
66
+ private dataStorage;
67
+ private http;
68
+ private store;
69
+ private engines;
70
+ private bandits;
71
+ private connected;
72
+ private flushInterval;
73
+ private flushInProgress;
74
+ private deadUuids;
75
+ private retryCounts;
76
+ constructor(options?: ClientOptions);
77
+ /**
78
+ * Bootstrap: authenticate and hydrate in-memory state from cloud.
79
+ *
80
+ * Resolves config from: constructor args → env vars → ~/.bandito/config.toml.
81
+ * Initializes WASM, creates HTTP client, SQLite store, fetches full state.
82
+ */
83
+ connect(): Promise<void>;
84
+ /**
85
+ * Local Thompson Sampling decision. Synchronous, <1ms, no network.
86
+ */
87
+ pull(banditName: string, options?: PullOptions): PullResult;
88
+ /**
89
+ * Record an LLM call outcome. Writes to SQLite first (crash-safe),
90
+ * then fires off a non-blocking flush to cloud.
91
+ */
92
+ update(pullResult: PullResult, options?: UpdateOptions): void;
93
+ /**
94
+ * Send a human grade for an existing event. Async (HTTP).
95
+ */
96
+ grade(eventId: string, grade: number): Promise<void>;
97
+ /**
98
+ * Explicit state refresh from cloud.
99
+ */
100
+ sync(): Promise<void>;
101
+ /**
102
+ * Shut down: clear interval, flush remaining events, close connections.
103
+ */
104
+ close(): Promise<void>;
105
+ private ensureConnected;
106
+ private applySync;
107
+ private flushPending;
108
+ }
109
+
110
+ /**
111
+ * Bandito SDK — contextual bandit optimization for LLM selection.
112
+ *
113
+ * Recommended (explicit client):
114
+ * import { BanditoClient } from 'bandito';
115
+ *
116
+ * const client = new BanditoClient({ apiKey: 'bnd_...' });
117
+ * await client.connect();
118
+ * const result = client.pull('my-chatbot', { query: userMessage });
119
+ * // ... call LLM with result.model, result.prompt ...
120
+ * client.update(result, { response: response.text });
121
+ * await client.close();
122
+ *
123
+ * Module-level singleton (convenience):
124
+ * import { connect, pull, update, close } from 'bandito';
125
+ *
126
+ * await connect({ apiKey: 'bnd_...' });
127
+ * const result = pull('my-chatbot', { query: userMessage });
128
+ * update(result, { response: response.text });
129
+ * await close();
130
+ */
131
+
132
+ /** Connect to the Bandito cloud and hydrate local state. */
133
+ declare function connect(options?: ClientOptions): Promise<void>;
134
+ /** Local Thompson Sampling decision. <1ms, no network. */
135
+ declare function pull(banditName: string, options?: PullOptions): PullResult;
136
+ /** Record an LLM call outcome (writes to SQLite, fire-and-forget flush). */
137
+ declare function update(pullResult: PullResult, options?: UpdateOptions): void;
138
+ /** Send a human grade for an existing event. */
139
+ declare function grade(eventId: string, gradeValue: number): Promise<void>;
140
+ /** Explicit state refresh from cloud. */
141
+ declare function sync(): Promise<void>;
142
+ /** Shut down: flush events, close connections. */
143
+ declare function close(): Promise<void>;
144
+
145
+ export { type Arm, BanditoClient, type ClientOptions, type PullOptions, type PullResult, type UpdateOptions, close, connect, grade, pull, sync, update };
@@ -0,0 +1,145 @@
1
+ /**
2
+ * SDK types: Arm, PullResult, and internal cache structures.
3
+ */
4
+ /** An arm returned to the user after pull(). Immutable. */
5
+ interface Arm {
6
+ readonly armId: number;
7
+ readonly modelName: string;
8
+ readonly modelProvider: string;
9
+ readonly systemPrompt: string;
10
+ readonly isPromptTemplated: boolean;
11
+ /** Convenience alias for modelName. */
12
+ readonly model: string;
13
+ /** Convenience alias for systemPrompt. */
14
+ readonly prompt: string;
15
+ }
16
+ /** Returned by pull(), passed to update(). Immutable. */
17
+ interface PullResult {
18
+ readonly arm: Arm;
19
+ readonly eventId: string;
20
+ readonly banditId: number;
21
+ readonly banditName: string;
22
+ readonly scores: Readonly<Record<number, number>>;
23
+ /** Convenience reach-through to arm.modelName. */
24
+ readonly model: string;
25
+ /** Convenience reach-through to arm.systemPrompt. */
26
+ readonly prompt: string;
27
+ /** @internal perf timestamp */
28
+ readonly _pullTime: number;
29
+ }
30
+
31
+ /**
32
+ * BanditoClient — main orchestrator for the JS/TS SDK.
33
+ *
34
+ * Mirrors the Python SDK's sync-first design:
35
+ * - pull() is synchronous (WASM math, <1ms)
36
+ * - connect(), grade(), sync(), close() are async (HTTP I/O)
37
+ * - update() is synchronous (SQLite write + fire-and-forget flush)
38
+ */
39
+
40
+ interface ClientOptions {
41
+ apiKey?: string;
42
+ baseUrl?: string;
43
+ storePath?: string;
44
+ dataStorage?: string;
45
+ }
46
+ interface PullOptions {
47
+ query?: string;
48
+ exclude?: number[];
49
+ }
50
+ interface UpdateOptions {
51
+ queryText?: string;
52
+ response?: string | Record<string, unknown>;
53
+ reward?: number;
54
+ cost?: number;
55
+ latency?: number;
56
+ inputTokens?: number;
57
+ outputTokens?: number;
58
+ segment?: Record<string, string>;
59
+ failed?: boolean;
60
+ }
61
+ declare class BanditoClient {
62
+ private apiKey;
63
+ private baseUrl;
64
+ private storePath;
65
+ private dataStorageArg;
66
+ private dataStorage;
67
+ private http;
68
+ private store;
69
+ private engines;
70
+ private bandits;
71
+ private connected;
72
+ private flushInterval;
73
+ private flushInProgress;
74
+ private deadUuids;
75
+ private retryCounts;
76
+ constructor(options?: ClientOptions);
77
+ /**
78
+ * Bootstrap: authenticate and hydrate in-memory state from cloud.
79
+ *
80
+ * Resolves config from: constructor args → env vars → ~/.bandito/config.toml.
81
+ * Initializes WASM, creates HTTP client, SQLite store, fetches full state.
82
+ */
83
+ connect(): Promise<void>;
84
+ /**
85
+ * Local Thompson Sampling decision. Synchronous, <1ms, no network.
86
+ */
87
+ pull(banditName: string, options?: PullOptions): PullResult;
88
+ /**
89
+ * Record an LLM call outcome. Writes to SQLite first (crash-safe),
90
+ * then fires off a non-blocking flush to cloud.
91
+ */
92
+ update(pullResult: PullResult, options?: UpdateOptions): void;
93
+ /**
94
+ * Send a human grade for an existing event. Async (HTTP).
95
+ */
96
+ grade(eventId: string, grade: number): Promise<void>;
97
+ /**
98
+ * Explicit state refresh from cloud.
99
+ */
100
+ sync(): Promise<void>;
101
+ /**
102
+ * Shut down: clear interval, flush remaining events, close connections.
103
+ */
104
+ close(): Promise<void>;
105
+ private ensureConnected;
106
+ private applySync;
107
+ private flushPending;
108
+ }
109
+
110
+ /**
111
+ * Bandito SDK — contextual bandit optimization for LLM selection.
112
+ *
113
+ * Recommended (explicit client):
114
+ * import { BanditoClient } from 'bandito';
115
+ *
116
+ * const client = new BanditoClient({ apiKey: 'bnd_...' });
117
+ * await client.connect();
118
+ * const result = client.pull('my-chatbot', { query: userMessage });
119
+ * // ... call LLM with result.model, result.prompt ...
120
+ * client.update(result, { response: response.text });
121
+ * await client.close();
122
+ *
123
+ * Module-level singleton (convenience):
124
+ * import { connect, pull, update, close } from 'bandito';
125
+ *
126
+ * await connect({ apiKey: 'bnd_...' });
127
+ * const result = pull('my-chatbot', { query: userMessage });
128
+ * update(result, { response: response.text });
129
+ * await close();
130
+ */
131
+
132
+ /** Connect to the Bandito cloud and hydrate local state. */
133
+ declare function connect(options?: ClientOptions): Promise<void>;
134
+ /** Local Thompson Sampling decision. <1ms, no network. */
135
+ declare function pull(banditName: string, options?: PullOptions): PullResult;
136
+ /** Record an LLM call outcome (writes to SQLite, fire-and-forget flush). */
137
+ declare function update(pullResult: PullResult, options?: UpdateOptions): void;
138
+ /** Send a human grade for an existing event. */
139
+ declare function grade(eventId: string, gradeValue: number): Promise<void>;
140
+ /** Explicit state refresh from cloud. */
141
+ declare function sync(): Promise<void>;
142
+ /** Shut down: flush events, close connections. */
143
+ declare function close(): Promise<void>;
144
+
145
+ export { type Arm, BanditoClient, type ClientOptions, type PullOptions, type PullResult, type UpdateOptions, close, connect, grade, pull, sync, update };