@blockrun/llm 0.2.1 → 1.0.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 CHANGED
@@ -2,7 +2,11 @@
2
2
 
3
3
  Pay-per-request access to GPT-4o, Claude 4, Gemini 2.5, and more via x402 micropayments on Base and Solana.
4
4
 
5
- **Networks:** Base (Chain ID: 8453) and Solana Mainnet
5
+ **Networks:**
6
+ - **Base Mainnet:** Chain ID 8453 - Production with real USDC
7
+ - **Base Sepolia (Testnet):** Chain ID 84532 - Developer testing with testnet USDC
8
+ - **Solana Mainnet** - Production with real USDC
9
+
6
10
  **Payment:** USDC
7
11
  **Protocol:** x402 v2 (CDP Facilitator)
8
12
 
@@ -55,12 +59,10 @@ For Solana, set `BLOCKRUN_SOLANA_KEY` environment variable with your base58-enco
55
59
  | Model | Input Price | Output Price |
56
60
  |-------|-------------|--------------|
57
61
  | `openai/gpt-5.2` | $1.75/M | $14.00/M |
58
- | `openai/gpt-5.1` | $1.25/M | $10.00/M |
59
- | `openai/gpt-5` | $1.25/M | $10.00/M |
60
62
  | `openai/gpt-5-mini` | $0.25/M | $2.00/M |
61
63
  | `openai/gpt-5-nano` | $0.05/M | $0.40/M |
62
64
  | `openai/gpt-5.2-pro` | $21.00/M | $168.00/M |
63
- | `openai/gpt-5-pro` | $15.00/M | $120.00/M |
65
+ | `openai/gpt-5.2-codex` | $1.75/M | $14.00/M |
64
66
 
65
67
  ### OpenAI GPT-4 Family
66
68
  | Model | Input Price | Output Price |
@@ -83,7 +85,10 @@ For Solana, set `BLOCKRUN_SOLANA_KEY` environment variable with your base58-enco
83
85
  ### Anthropic Claude
84
86
  | Model | Input Price | Output Price |
85
87
  |-------|-------------|--------------|
88
+ | `anthropic/claude-opus-4.6` | $5.00/M | $25.00/M |
89
+ | `anthropic/claude-opus-4.5` | $5.00/M | $25.00/M |
86
90
  | `anthropic/claude-opus-4` | $15.00/M | $75.00/M |
91
+ | `anthropic/claude-sonnet-4.6` | $3.00/M | $15.00/M |
87
92
  | `anthropic/claude-sonnet-4` | $3.00/M | $15.00/M |
88
93
  | `anthropic/claude-haiku-4.5` | $1.00/M | $5.00/M |
89
94
 
@@ -92,7 +97,52 @@ For Solana, set `BLOCKRUN_SOLANA_KEY` environment variable with your base58-enco
92
97
  |-------|-------------|--------------|
93
98
  | `google/gemini-3-pro-preview` | $2.00/M | $12.00/M |
94
99
  | `google/gemini-2.5-pro` | $1.25/M | $10.00/M |
95
- | `google/gemini-2.5-flash` | $0.15/M | $0.60/M |
100
+ | `google/gemini-2.5-flash` | $0.30/M | $2.50/M |
101
+
102
+ ### DeepSeek
103
+ | Model | Input Price | Output Price |
104
+ |-------|-------------|--------------|
105
+ | `deepseek/deepseek-chat` | $0.28/M | $0.42/M |
106
+ | `deepseek/deepseek-reasoner` | $0.28/M | $0.42/M |
107
+
108
+ ### xAI Grok
109
+ | Model | Input Price | Output Price | Context | Notes |
110
+ |-------|-------------|--------------|---------|-------|
111
+ | `xai/grok-3` | $3.00/M | $15.00/M | 131K | Flagship |
112
+ | `xai/grok-3-mini` | $0.30/M | $0.50/M | 131K | Fast & affordable |
113
+ | `xai/grok-4-1-fast-reasoning` | $0.20/M | $0.50/M | **2M** | Latest, chain-of-thought |
114
+ | `xai/grok-4-1-fast-non-reasoning` | $0.20/M | $0.50/M | **2M** | Latest, direct response |
115
+ | `xai/grok-4-fast-reasoning` | $0.20/M | $0.50/M | **2M** | Step-by-step reasoning |
116
+ | `xai/grok-4-fast-non-reasoning` | $0.20/M | $0.50/M | **2M** | Quick responses |
117
+ | `xai/grok-code-fast-1` | $0.20/M | $1.50/M | 256K | Code generation |
118
+ | `xai/grok-4-0709` | $0.20/M | $1.50/M | 256K | Premium quality |
119
+ | `xai/grok-2-vision` | $2.00/M | $10.00/M | 32K | Vision capabilities |
120
+
121
+ ### Moonshot Kimi
122
+ | Model | Input Price | Output Price |
123
+ |-------|-------------|--------------|
124
+ | `moonshot/kimi-k2.5` | $0.60/M | $3.00/M |
125
+
126
+ ### NVIDIA (Free & Hosted)
127
+ | Model | Input Price | Output Price | Notes |
128
+ |-------|-------------|--------------|-------|
129
+ | `nvidia/gpt-oss-120b` | **FREE** | **FREE** | OpenAI open-weight 120B (Apache 2.0) |
130
+ | `nvidia/kimi-k2.5` | $0.60/M | $3.00/M | Moonshot 1T MoE with vision |
131
+
132
+ ### E2E Verified Models
133
+
134
+ All models below have been tested end-to-end via the TypeScript SDK (Feb 2026):
135
+
136
+ | Provider | Model | Status |
137
+ |----------|-------|--------|
138
+ | OpenAI | `openai/gpt-4o-mini` | Passed |
139
+ | OpenAI | `openai/gpt-5.2-codex` | Passed |
140
+ | Anthropic | `anthropic/claude-opus-4.6` | Passed |
141
+ | Anthropic | `anthropic/claude-sonnet-4` | Passed |
142
+ | Google | `google/gemini-2.5-flash` | Passed |
143
+ | DeepSeek | `deepseek/deepseek-chat` | Passed |
144
+ | xAI | `xai/grok-3` | Passed |
145
+ | Moonshot | `moonshot/kimi-k2.5` | Passed |
96
146
 
97
147
  ### Image Generation
98
148
  | Model | Price |
@@ -103,6 +153,56 @@ For Solana, set `BLOCKRUN_SOLANA_KEY` environment variable with your base58-enco
103
153
  | `google/nano-banana-pro` | $0.10-0.15/image |
104
154
  | `black-forest/flux-1.1-pro` | $0.04/image |
105
155
 
156
+ ### Testnet Models (Base Sepolia)
157
+ | Model | Price |
158
+ |-------|-------|
159
+ | `openai/gpt-oss-20b` | $0.001/request |
160
+ | `openai/gpt-oss-120b` | $0.002/request |
161
+
162
+ *Testnet models use flat pricing (no token counting) for simplicity.*
163
+
164
+ ## Testnet Usage
165
+
166
+ For development and testing without real USDC, use the testnet:
167
+
168
+ ```typescript
169
+ import { testnetClient } from '@blockrun/llm';
170
+
171
+ // Create testnet client (uses Base Sepolia)
172
+ const client = testnetClient({ privateKey: '0x...' });
173
+
174
+ // Chat with testnet model
175
+ const response = await client.chat('openai/gpt-oss-20b', 'Hello!');
176
+ console.log(response);
177
+
178
+ // Check if client is on testnet
179
+ console.log(client.isTestnet()); // true
180
+ ```
181
+
182
+ ### Testnet Setup
183
+
184
+ 1. Get testnet ETH from [Alchemy Base Sepolia Faucet](https://www.alchemy.com/faucets/base-sepolia)
185
+ 2. Get testnet USDC from [Circle USDC Faucet](https://faucet.circle.com/)
186
+ 3. Set your wallet key: `export BASE_CHAIN_WALLET_KEY=0x...`
187
+
188
+ ### Available Testnet Models
189
+
190
+ - `openai/gpt-oss-20b` - $0.001/request (flat price)
191
+ - `openai/gpt-oss-120b` - $0.002/request (flat price)
192
+
193
+ ### Manual Testnet Configuration
194
+
195
+ ```typescript
196
+ import { LLMClient } from '@blockrun/llm';
197
+
198
+ // Or configure manually
199
+ const client = new LLMClient({
200
+ privateKey: '0x...',
201
+ apiUrl: 'https://testnet.blockrun.ai/api'
202
+ });
203
+ const response = await client.chat('openai/gpt-oss-20b', 'Hello!');
204
+ ```
205
+
106
206
  ## Usage Examples
107
207
 
108
208
  ### Simple Chat
@@ -121,6 +221,47 @@ const response2 = await client.chat('anthropic/claude-sonnet-4', 'Write a haiku'
121
221
  });
122
222
  ```
123
223
 
224
+ ### Smart Routing (ClawRouter)
225
+
226
+ Save up to 78% on inference costs with intelligent model routing. ClawRouter uses a 14-dimension rule-based scoring algorithm to select the cheapest model that can handle your request (<1ms, 100% local).
227
+
228
+ ```typescript
229
+ import { LLMClient } from '@blockrun/llm';
230
+
231
+ const client = new LLMClient();
232
+
233
+ // Auto-route to cheapest capable model
234
+ const result = await client.smartChat('What is 2+2?');
235
+ console.log(result.response); // '4'
236
+ console.log(result.model); // 'google/gemini-2.5-flash'
237
+ console.log(result.routing.tier); // 'SIMPLE'
238
+ console.log(`Saved ${(result.routing.savings * 100).toFixed(0)}%`); // 'Saved 78%'
239
+
240
+ // Routing profiles
241
+ const free = await client.smartChat('Hello!', { routingProfile: 'free' }); // Zero cost
242
+ const eco = await client.smartChat('Explain AI', { routingProfile: 'eco' }); // Budget optimized
243
+ const auto = await client.smartChat('Code review', { routingProfile: 'auto' }); // Balanced (default)
244
+ const premium = await client.smartChat('Write a legal brief', { routingProfile: 'premium' }); // Best quality
245
+ ```
246
+
247
+ **Routing Profiles:**
248
+
249
+ | Profile | Description | Best For |
250
+ |---------|-------------|----------|
251
+ | `free` | NVIDIA free models only | Testing, simple queries |
252
+ | `eco` | Budget-optimized | Cost-sensitive workloads |
253
+ | `auto` | Intelligent routing (default) | General use |
254
+ | `premium` | Best quality models | Critical tasks |
255
+
256
+ **Tiers:**
257
+
258
+ | Tier | Example Tasks | Typical Models |
259
+ |------|---------------|----------------|
260
+ | SIMPLE | Greetings, math, lookups | Gemini Flash, GPT-4o-mini |
261
+ | MEDIUM | Explanations, summaries | GPT-4o, Claude Sonnet |
262
+ | COMPLEX | Analysis, code generation | GPT-5.2, Claude Opus |
263
+ | REASONING | Multi-step logic, planning | o3, DeepSeek Reasoner |
264
+
124
265
  ### Full Chat Completion
125
266
 
126
267
  ```typescript
@@ -299,10 +440,17 @@ Full TypeScript support with exported types:
299
440
  ```typescript
300
441
  import {
301
442
  LLMClient,
443
+ testnetClient,
302
444
  type ChatMessage,
303
445
  type ChatResponse,
304
446
  type ChatOptions,
305
447
  type Model,
448
+ // Smart routing types
449
+ type SmartChatOptions,
450
+ type SmartChatResponse,
451
+ type RoutingDecision,
452
+ type RoutingProfile,
453
+ type RoutingTier,
306
454
  APIError,
307
455
  PaymentError,
308
456
  } from '@blockrun/llm';
@@ -311,9 +459,9 @@ import {
311
459
  ## Links
312
460
 
313
461
  - [Website](https://blockrun.ai)
314
- - [Documentation](https://docs.blockrun.ai)
315
- - [GitHub](https://github.com/blockrun/blockrun-llm-ts)
316
- - [Discord](https://discord.gg/blockrun)
462
+ - [Documentation](https://github.com/BlockRunAI/awesome-blockrun/tree/main/docs)
463
+ - [GitHub](https://github.com/blockrunai/blockrun-llm-ts)
464
+ - [Telegram](https://t.me/+mroQv4-4hGgzOGUx)
317
465
 
318
466
  ## License
319
467
 
package/dist/index.cjs CHANGED
@@ -51,7 +51,8 @@ __export(index_exports, {
51
51
  getPaymentLinks: () => getPaymentLinks,
52
52
  getWalletAddress: () => getWalletAddress,
53
53
  loadWallet: () => loadWallet,
54
- saveWallet: () => saveWallet
54
+ saveWallet: () => saveWallet,
55
+ testnetClient: () => testnetClient
55
56
  });
56
57
  module.exports = __toCommonJS(index_exports);
57
58
 
@@ -82,6 +83,9 @@ var APIError = class extends BlockrunError {
82
83
  }
83
84
  };
84
85
 
86
+ // src/client.ts
87
+ var import_clawrouter = require("@blockrun/clawrouter");
88
+
85
89
  // src/x402.ts
86
90
  var import_accounts = require("viem/accounts");
87
91
  var BASE_CHAIN_ID = 8453;
@@ -278,15 +282,22 @@ function validateResourceUrl(url, baseUrl) {
278
282
 
279
283
  // src/client.ts
280
284
  var DEFAULT_API_URL = "https://blockrun.ai/api";
285
+ var TESTNET_API_URL = "https://testnet.blockrun.ai/api";
281
286
  var DEFAULT_MAX_TOKENS = 1024;
282
287
  var DEFAULT_TIMEOUT = 6e4;
288
+ var SDK_VERSION = "0.3.0";
289
+ var USER_AGENT = `blockrun-ts/${SDK_VERSION}`;
283
290
  var LLMClient = class {
291
+ static DEFAULT_API_URL = DEFAULT_API_URL;
292
+ static TESTNET_API_URL = TESTNET_API_URL;
284
293
  account;
285
294
  privateKey;
286
295
  apiUrl;
287
296
  timeout;
288
297
  sessionTotalUsd = 0;
289
298
  sessionCalls = 0;
299
+ modelPricingCache = null;
300
+ modelPricingPromise = null;
290
301
  /**
291
302
  * Initialize the BlockRun LLM client.
292
303
  *
@@ -333,7 +344,92 @@ var LLMClient = class {
333
344
  search: options?.search,
334
345
  searchParameters: options?.searchParameters
335
346
  });
336
- return result.choices[0].message.content;
347
+ return result.choices[0].message.content || "";
348
+ }
349
+ /**
350
+ * Smart chat with automatic model routing.
351
+ *
352
+ * Uses ClawRouter's 14-dimension rule-based scoring algorithm (<1ms, 100% local)
353
+ * to select the cheapest model that can handle your request.
354
+ *
355
+ * @param prompt - User message
356
+ * @param options - Optional chat and routing parameters
357
+ * @returns SmartChatResponse with response text, selected model, and routing metadata
358
+ *
359
+ * @example Simple usage (auto profile)
360
+ * ```ts
361
+ * const result = await client.smartChat('What is 2+2?');
362
+ * console.log(result.response); // '4'
363
+ * console.log(result.model); // 'google/gemini-2.5-flash'
364
+ * console.log(result.routing.savings); // 0.78 (78% savings)
365
+ * ```
366
+ *
367
+ * @example With routing profile
368
+ * ```ts
369
+ * // Free tier only (zero cost)
370
+ * const result = await client.smartChat('Hello!', { routingProfile: 'free' });
371
+ *
372
+ * // Eco mode (budget optimized)
373
+ * const result = await client.smartChat('Explain quantum computing', { routingProfile: 'eco' });
374
+ *
375
+ * // Premium mode (best quality)
376
+ * const result = await client.smartChat('Write a business plan', { routingProfile: 'premium' });
377
+ * ```
378
+ */
379
+ async smartChat(prompt, options) {
380
+ const modelPricing = await this.getModelPricing();
381
+ const maxOutputTokens = options?.maxOutputTokens || options?.maxTokens || 1024;
382
+ const decision = (0, import_clawrouter.route)(prompt, options?.system, maxOutputTokens, {
383
+ config: import_clawrouter.DEFAULT_ROUTING_CONFIG,
384
+ modelPricing,
385
+ routingProfile: options?.routingProfile
386
+ });
387
+ const response = await this.chat(decision.model, prompt, {
388
+ system: options?.system,
389
+ maxTokens: options?.maxTokens,
390
+ temperature: options?.temperature,
391
+ topP: options?.topP,
392
+ search: options?.search,
393
+ searchParameters: options?.searchParameters
394
+ });
395
+ return {
396
+ response,
397
+ model: decision.model,
398
+ routing: decision
399
+ };
400
+ }
401
+ /**
402
+ * Get model pricing map (cached).
403
+ * Fetches from API on first call, then returns cached result.
404
+ */
405
+ async getModelPricing() {
406
+ if (this.modelPricingCache) {
407
+ return this.modelPricingCache;
408
+ }
409
+ if (this.modelPricingPromise) {
410
+ return this.modelPricingPromise;
411
+ }
412
+ this.modelPricingPromise = this.fetchModelPricing();
413
+ try {
414
+ this.modelPricingCache = await this.modelPricingPromise;
415
+ return this.modelPricingCache;
416
+ } finally {
417
+ this.modelPricingPromise = null;
418
+ }
419
+ }
420
+ /**
421
+ * Fetch model pricing from API.
422
+ */
423
+ async fetchModelPricing() {
424
+ const models = await this.listModels();
425
+ const pricing = /* @__PURE__ */ new Map();
426
+ for (const model of models) {
427
+ pricing.set(model.id, {
428
+ inputPrice: model.inputPrice,
429
+ outputPrice: model.outputPrice
430
+ });
431
+ }
432
+ return pricing;
337
433
  }
338
434
  /**
339
435
  * Full chat completion interface (OpenAI-compatible).
@@ -360,6 +456,12 @@ var LLMClient = class {
360
456
  } else if (options?.search === true) {
361
457
  body.search_parameters = { mode: "on" };
362
458
  }
459
+ if (options?.tools !== void 0) {
460
+ body.tools = options.tools;
461
+ }
462
+ if (options?.toolChoice !== void 0) {
463
+ body.tool_choice = options.toolChoice;
464
+ }
363
465
  return this.requestWithPayment("/v1/chat/completions", body);
364
466
  }
365
467
  /**
@@ -369,7 +471,7 @@ var LLMClient = class {
369
471
  const url = `${this.apiUrl}${endpoint}`;
370
472
  const response = await this.fetchWithTimeout(url, {
371
473
  method: "POST",
372
- headers: { "Content-Type": "application/json" },
474
+ headers: { "Content-Type": "application/json", "User-Agent": USER_AGENT },
373
475
  body: JSON.stringify(body)
374
476
  });
375
477
  if (response.status === 402) {
@@ -432,6 +534,7 @@ var LLMClient = class {
432
534
  method: "POST",
433
535
  headers: {
434
536
  "Content-Type": "application/json",
537
+ "User-Agent": USER_AGENT,
435
538
  "PAYMENT-SIGNATURE": paymentPayload
436
539
  },
437
540
  body: JSON.stringify(body)
@@ -560,7 +663,19 @@ var LLMClient = class {
560
663
  getWalletAddress() {
561
664
  return this.account.address;
562
665
  }
666
+ /**
667
+ * Check if client is configured for testnet.
668
+ */
669
+ isTestnet() {
670
+ return this.apiUrl.includes("testnet.blockrun.ai");
671
+ }
563
672
  };
673
+ function testnetClient(options = {}) {
674
+ return new LLMClient({
675
+ ...options,
676
+ apiUrl: TESTNET_API_URL
677
+ });
678
+ }
564
679
  var client_default = LLMClient;
565
680
 
566
681
  // src/image.ts
@@ -962,7 +1077,9 @@ var ChatCompletions = class {
962
1077
  {
963
1078
  maxTokens: params.max_tokens,
964
1079
  temperature: params.temperature,
965
- topP: params.top_p
1080
+ topP: params.top_p,
1081
+ tools: params.tools,
1082
+ toolChoice: params.tool_choice
966
1083
  }
967
1084
  );
968
1085
  return this.transformResponse(response);
@@ -977,6 +1094,12 @@ var ChatCompletions = class {
977
1094
  top_p: params.top_p,
978
1095
  stream: true
979
1096
  };
1097
+ if (params.tools) {
1098
+ body.tools = params.tools;
1099
+ }
1100
+ if (params.tool_choice) {
1101
+ body.tool_choice = params.tool_choice;
1102
+ }
980
1103
  const controller = new AbortController();
981
1104
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
982
1105
  try {
@@ -1013,7 +1136,8 @@ var ChatCompletions = class {
1013
1136
  index: choice.index ?? index,
1014
1137
  message: {
1015
1138
  role: "assistant",
1016
- content: choice.message.content
1139
+ content: choice.message.content,
1140
+ tool_calls: choice.message.tool_calls
1017
1141
  },
1018
1142
  finish_reason: choice.finish_reason || "stop"
1019
1143
  })),
@@ -1070,5 +1194,6 @@ var OpenAI = class {
1070
1194
  getPaymentLinks,
1071
1195
  getWalletAddress,
1072
1196
  loadWallet,
1073
- saveWallet
1197
+ saveWallet,
1198
+ testnetClient
1074
1199
  });
package/dist/index.d.cts CHANGED
@@ -1,14 +1,42 @@
1
1
  /**
2
2
  * Type definitions for BlockRun LLM SDK
3
3
  */
4
+ interface FunctionDefinition {
5
+ name: string;
6
+ description?: string;
7
+ parameters?: Record<string, unknown>;
8
+ strict?: boolean;
9
+ }
10
+ interface Tool {
11
+ type: "function";
12
+ function: FunctionDefinition;
13
+ }
14
+ interface FunctionCall {
15
+ name: string;
16
+ arguments: string;
17
+ }
18
+ interface ToolCall {
19
+ id: string;
20
+ type: "function";
21
+ function: FunctionCall;
22
+ }
23
+ type ToolChoice = "none" | "auto" | "required" | {
24
+ type: "function";
25
+ function: {
26
+ name: string;
27
+ };
28
+ };
4
29
  interface ChatMessage {
5
- role: "system" | "user" | "assistant";
6
- content: string;
30
+ role: "system" | "user" | "assistant" | "tool";
31
+ content?: string | null;
32
+ name?: string;
33
+ tool_call_id?: string;
34
+ tool_calls?: ToolCall[];
7
35
  }
8
36
  interface ChatChoice {
9
37
  index: number;
10
38
  message: ChatMessage;
11
- finish_reason?: string;
39
+ finish_reason?: "stop" | "length" | "content_filter" | "tool_calls";
12
40
  }
13
41
  interface ChatUsage {
14
42
  prompt_tokens: number;
@@ -146,6 +174,36 @@ interface ChatCompletionOptions {
146
174
  search?: boolean;
147
175
  /** Full xAI Live Search configuration (for Grok models) */
148
176
  searchParameters?: SearchParameters;
177
+ /** Tool definitions for function calling */
178
+ tools?: Tool[];
179
+ /** Tool selection strategy */
180
+ toolChoice?: ToolChoice;
181
+ }
182
+ type RoutingProfile = "free" | "eco" | "auto" | "premium";
183
+ type RoutingTier = "SIMPLE" | "MEDIUM" | "COMPLEX" | "REASONING";
184
+ interface RoutingDecision {
185
+ model: string;
186
+ tier: RoutingTier;
187
+ confidence: number;
188
+ method: "rules" | "llm";
189
+ reasoning: string;
190
+ costEstimate: number;
191
+ baselineCost: number;
192
+ savings: number;
193
+ }
194
+ interface SmartChatOptions extends ChatOptions {
195
+ /** Routing profile: free (zero cost), eco (budget), auto (balanced), premium (best quality) */
196
+ routingProfile?: RoutingProfile;
197
+ /** Maximum output tokens (used for cost estimation) */
198
+ maxOutputTokens?: number;
199
+ }
200
+ interface SmartChatResponse {
201
+ /** The AI response text */
202
+ response: string;
203
+ /** Which model was selected by smart routing */
204
+ model: string;
205
+ /** Routing decision metadata */
206
+ routing: RoutingDecision;
149
207
  }
150
208
  declare class BlockrunError extends Error {
151
209
  constructor(message: string);
@@ -180,14 +238,36 @@ declare class APIError extends BlockrunError {
180
238
  *
181
239
  * Provides access to multiple LLM providers (OpenAI, Anthropic, Google, etc.)
182
240
  * with automatic x402 micropayments on Base chain.
241
+ *
242
+ * Networks:
243
+ * - Mainnet: https://blockrun.ai/api (Base, Chain ID 8453)
244
+ * - Testnet: https://testnet.blockrun.ai/api (Base Sepolia, Chain ID 84532)
245
+ *
246
+ * @example Testnet usage
247
+ * ```ts
248
+ * // Use testnet convenience function
249
+ * import { testnetClient } from '@blockrun/llm';
250
+ * const client = testnetClient({ privateKey: '0x...' });
251
+ * const response = await client.chat('openai/gpt-oss-20b', 'Hello!');
252
+ *
253
+ * // Or configure manually
254
+ * const client = new LLMClient({
255
+ * privateKey: '0x...',
256
+ * apiUrl: 'https://testnet.blockrun.ai/api'
257
+ * });
258
+ * ```
183
259
  */
184
260
  declare class LLMClient {
261
+ static readonly DEFAULT_API_URL = "https://blockrun.ai/api";
262
+ static readonly TESTNET_API_URL = "https://testnet.blockrun.ai/api";
185
263
  private account;
186
264
  private privateKey;
187
265
  private apiUrl;
188
266
  private timeout;
189
267
  private sessionTotalUsd;
190
268
  private sessionCalls;
269
+ private modelPricingCache;
270
+ private modelPricingPromise;
191
271
  /**
192
272
  * Initialize the BlockRun LLM client.
193
273
  *
@@ -207,6 +287,46 @@ declare class LLMClient {
207
287
  * console.log(response); // 'The capital of France is Paris.'
208
288
  */
209
289
  chat(model: string, prompt: string, options?: ChatOptions): Promise<string>;
290
+ /**
291
+ * Smart chat with automatic model routing.
292
+ *
293
+ * Uses ClawRouter's 14-dimension rule-based scoring algorithm (<1ms, 100% local)
294
+ * to select the cheapest model that can handle your request.
295
+ *
296
+ * @param prompt - User message
297
+ * @param options - Optional chat and routing parameters
298
+ * @returns SmartChatResponse with response text, selected model, and routing metadata
299
+ *
300
+ * @example Simple usage (auto profile)
301
+ * ```ts
302
+ * const result = await client.smartChat('What is 2+2?');
303
+ * console.log(result.response); // '4'
304
+ * console.log(result.model); // 'google/gemini-2.5-flash'
305
+ * console.log(result.routing.savings); // 0.78 (78% savings)
306
+ * ```
307
+ *
308
+ * @example With routing profile
309
+ * ```ts
310
+ * // Free tier only (zero cost)
311
+ * const result = await client.smartChat('Hello!', { routingProfile: 'free' });
312
+ *
313
+ * // Eco mode (budget optimized)
314
+ * const result = await client.smartChat('Explain quantum computing', { routingProfile: 'eco' });
315
+ *
316
+ * // Premium mode (best quality)
317
+ * const result = await client.smartChat('Write a business plan', { routingProfile: 'premium' });
318
+ * ```
319
+ */
320
+ smartChat(prompt: string, options?: SmartChatOptions): Promise<SmartChatResponse>;
321
+ /**
322
+ * Get model pricing map (cached).
323
+ * Fetches from API on first call, then returns cached result.
324
+ */
325
+ private getModelPricing;
326
+ /**
327
+ * Fetch model pricing from API.
328
+ */
329
+ private fetchModelPricing;
210
330
  /**
211
331
  * Full chat completion interface (OpenAI-compatible).
212
332
  *
@@ -266,7 +386,38 @@ declare class LLMClient {
266
386
  * Get the wallet address being used for payments.
267
387
  */
268
388
  getWalletAddress(): string;
389
+ /**
390
+ * Check if client is configured for testnet.
391
+ */
392
+ isTestnet(): boolean;
269
393
  }
394
+ /**
395
+ * Create a testnet LLM client for development and testing.
396
+ *
397
+ * This is a convenience function that creates an LLMClient configured
398
+ * for the BlockRun testnet (Base Sepolia).
399
+ *
400
+ * @param options - Client options (privateKey required unless BASE_CHAIN_WALLET_KEY env var is set)
401
+ * @returns LLMClient configured for testnet
402
+ *
403
+ * @example
404
+ * ```ts
405
+ * import { testnetClient } from '@blockrun/llm';
406
+ *
407
+ * const client = testnetClient({ privateKey: '0x...' });
408
+ * const response = await client.chat('openai/gpt-oss-20b', 'Hello!');
409
+ * ```
410
+ *
411
+ * Testnet Setup:
412
+ * 1. Get testnet ETH from https://www.alchemy.com/faucets/base-sepolia
413
+ * 2. Get testnet USDC from https://faucet.circle.com/
414
+ * 3. Use your wallet with testnet funds
415
+ *
416
+ * Available Testnet Models:
417
+ * - openai/gpt-oss-20b
418
+ * - openai/gpt-oss-120b
419
+ */
420
+ declare function testnetClient(options?: Omit<LLMClientOptions, 'apiUrl'>): LLMClient;
270
421
 
271
422
  /**
272
423
  * BlockRun Image Client - Generate images via x402 micropayments.
@@ -485,8 +636,11 @@ interface OpenAIClientOptions {
485
636
  interface OpenAIChatCompletionParams {
486
637
  model: string;
487
638
  messages: Array<{
488
- role: "system" | "user" | "assistant";
489
- content: string;
639
+ role: "system" | "user" | "assistant" | "tool";
640
+ content?: string | null;
641
+ name?: string;
642
+ tool_call_id?: string;
643
+ tool_calls?: ToolCall[];
490
644
  }>;
491
645
  max_tokens?: number;
492
646
  temperature?: number;
@@ -497,14 +651,17 @@ interface OpenAIChatCompletionParams {
497
651
  presence_penalty?: number;
498
652
  frequency_penalty?: number;
499
653
  user?: string;
654
+ tools?: Tool[];
655
+ tool_choice?: ToolChoice;
500
656
  }
501
657
  interface OpenAIChatCompletionChoice {
502
658
  index: number;
503
659
  message: {
504
660
  role: "assistant";
505
- content: string;
661
+ content?: string | null;
662
+ tool_calls?: ToolCall[];
506
663
  };
507
- finish_reason: string | null;
664
+ finish_reason: "stop" | "length" | "content_filter" | "tool_calls" | null;
508
665
  }
509
666
  interface OpenAIChatCompletionResponse {
510
667
  id: string;
@@ -584,4 +741,4 @@ declare class OpenAI {
584
741
  getWalletAddress(): string;
585
742
  }
586
743
 
587
- export { APIError, BASE_CHAIN_ID, BlockrunError, type ChatChoice, type ChatCompletionOptions, type ChatMessage, type ChatOptions, type ChatResponse, type ChatUsage, ImageClient, type ImageClientOptions, type ImageData, type ImageGenerateOptions, type ImageModel, type ImageResponse, LLMClient, type LLMClientOptions, type Model, type NewsSearchSource, OpenAI, type OpenAIChatCompletionChoice, type OpenAIChatCompletionChunk, type OpenAIChatCompletionParams, type OpenAIChatCompletionResponse, type OpenAIClientOptions, PaymentError, type PaymentLinks, type RssSearchSource, type SearchParameters, type SearchSource, type Spending, USDC_BASE, USDC_BASE_CONTRACT, WALLET_DIR_PATH, WALLET_FILE_PATH, type WalletInfo, type WebSearchSource, type XSearchSource, createWallet, LLMClient as default, formatFundingMessageCompact, formatNeedsFundingMessage, formatWalletCreatedMessage, getEip681Uri, getOrCreateWallet, getPaymentLinks, getWalletAddress, loadWallet, saveWallet };
744
+ export { APIError, BASE_CHAIN_ID, BlockrunError, type ChatChoice, type ChatCompletionOptions, type ChatMessage, type ChatOptions, type ChatResponse, type ChatUsage, type FunctionCall, type FunctionDefinition, ImageClient, type ImageClientOptions, type ImageData, type ImageGenerateOptions, type ImageModel, type ImageResponse, LLMClient, type LLMClientOptions, type Model, type NewsSearchSource, OpenAI, type OpenAIChatCompletionChoice, type OpenAIChatCompletionChunk, type OpenAIChatCompletionParams, type OpenAIChatCompletionResponse, type OpenAIClientOptions, PaymentError, type PaymentLinks, type RoutingDecision, type RoutingProfile, type RoutingTier, type RssSearchSource, type SearchParameters, type SearchSource, type SmartChatOptions, type SmartChatResponse, type Spending, type Tool, type ToolCall, type ToolChoice, USDC_BASE, USDC_BASE_CONTRACT, WALLET_DIR_PATH, WALLET_FILE_PATH, type WalletInfo, type WebSearchSource, type XSearchSource, createWallet, LLMClient as default, formatFundingMessageCompact, formatNeedsFundingMessage, formatWalletCreatedMessage, getEip681Uri, getOrCreateWallet, getPaymentLinks, getWalletAddress, loadWallet, saveWallet, testnetClient };
package/dist/index.d.ts CHANGED
@@ -1,14 +1,42 @@
1
1
  /**
2
2
  * Type definitions for BlockRun LLM SDK
3
3
  */
4
+ interface FunctionDefinition {
5
+ name: string;
6
+ description?: string;
7
+ parameters?: Record<string, unknown>;
8
+ strict?: boolean;
9
+ }
10
+ interface Tool {
11
+ type: "function";
12
+ function: FunctionDefinition;
13
+ }
14
+ interface FunctionCall {
15
+ name: string;
16
+ arguments: string;
17
+ }
18
+ interface ToolCall {
19
+ id: string;
20
+ type: "function";
21
+ function: FunctionCall;
22
+ }
23
+ type ToolChoice = "none" | "auto" | "required" | {
24
+ type: "function";
25
+ function: {
26
+ name: string;
27
+ };
28
+ };
4
29
  interface ChatMessage {
5
- role: "system" | "user" | "assistant";
6
- content: string;
30
+ role: "system" | "user" | "assistant" | "tool";
31
+ content?: string | null;
32
+ name?: string;
33
+ tool_call_id?: string;
34
+ tool_calls?: ToolCall[];
7
35
  }
8
36
  interface ChatChoice {
9
37
  index: number;
10
38
  message: ChatMessage;
11
- finish_reason?: string;
39
+ finish_reason?: "stop" | "length" | "content_filter" | "tool_calls";
12
40
  }
13
41
  interface ChatUsage {
14
42
  prompt_tokens: number;
@@ -146,6 +174,36 @@ interface ChatCompletionOptions {
146
174
  search?: boolean;
147
175
  /** Full xAI Live Search configuration (for Grok models) */
148
176
  searchParameters?: SearchParameters;
177
+ /** Tool definitions for function calling */
178
+ tools?: Tool[];
179
+ /** Tool selection strategy */
180
+ toolChoice?: ToolChoice;
181
+ }
182
+ type RoutingProfile = "free" | "eco" | "auto" | "premium";
183
+ type RoutingTier = "SIMPLE" | "MEDIUM" | "COMPLEX" | "REASONING";
184
+ interface RoutingDecision {
185
+ model: string;
186
+ tier: RoutingTier;
187
+ confidence: number;
188
+ method: "rules" | "llm";
189
+ reasoning: string;
190
+ costEstimate: number;
191
+ baselineCost: number;
192
+ savings: number;
193
+ }
194
+ interface SmartChatOptions extends ChatOptions {
195
+ /** Routing profile: free (zero cost), eco (budget), auto (balanced), premium (best quality) */
196
+ routingProfile?: RoutingProfile;
197
+ /** Maximum output tokens (used for cost estimation) */
198
+ maxOutputTokens?: number;
199
+ }
200
+ interface SmartChatResponse {
201
+ /** The AI response text */
202
+ response: string;
203
+ /** Which model was selected by smart routing */
204
+ model: string;
205
+ /** Routing decision metadata */
206
+ routing: RoutingDecision;
149
207
  }
150
208
  declare class BlockrunError extends Error {
151
209
  constructor(message: string);
@@ -180,14 +238,36 @@ declare class APIError extends BlockrunError {
180
238
  *
181
239
  * Provides access to multiple LLM providers (OpenAI, Anthropic, Google, etc.)
182
240
  * with automatic x402 micropayments on Base chain.
241
+ *
242
+ * Networks:
243
+ * - Mainnet: https://blockrun.ai/api (Base, Chain ID 8453)
244
+ * - Testnet: https://testnet.blockrun.ai/api (Base Sepolia, Chain ID 84532)
245
+ *
246
+ * @example Testnet usage
247
+ * ```ts
248
+ * // Use testnet convenience function
249
+ * import { testnetClient } from '@blockrun/llm';
250
+ * const client = testnetClient({ privateKey: '0x...' });
251
+ * const response = await client.chat('openai/gpt-oss-20b', 'Hello!');
252
+ *
253
+ * // Or configure manually
254
+ * const client = new LLMClient({
255
+ * privateKey: '0x...',
256
+ * apiUrl: 'https://testnet.blockrun.ai/api'
257
+ * });
258
+ * ```
183
259
  */
184
260
  declare class LLMClient {
261
+ static readonly DEFAULT_API_URL = "https://blockrun.ai/api";
262
+ static readonly TESTNET_API_URL = "https://testnet.blockrun.ai/api";
185
263
  private account;
186
264
  private privateKey;
187
265
  private apiUrl;
188
266
  private timeout;
189
267
  private sessionTotalUsd;
190
268
  private sessionCalls;
269
+ private modelPricingCache;
270
+ private modelPricingPromise;
191
271
  /**
192
272
  * Initialize the BlockRun LLM client.
193
273
  *
@@ -207,6 +287,46 @@ declare class LLMClient {
207
287
  * console.log(response); // 'The capital of France is Paris.'
208
288
  */
209
289
  chat(model: string, prompt: string, options?: ChatOptions): Promise<string>;
290
+ /**
291
+ * Smart chat with automatic model routing.
292
+ *
293
+ * Uses ClawRouter's 14-dimension rule-based scoring algorithm (<1ms, 100% local)
294
+ * to select the cheapest model that can handle your request.
295
+ *
296
+ * @param prompt - User message
297
+ * @param options - Optional chat and routing parameters
298
+ * @returns SmartChatResponse with response text, selected model, and routing metadata
299
+ *
300
+ * @example Simple usage (auto profile)
301
+ * ```ts
302
+ * const result = await client.smartChat('What is 2+2?');
303
+ * console.log(result.response); // '4'
304
+ * console.log(result.model); // 'google/gemini-2.5-flash'
305
+ * console.log(result.routing.savings); // 0.78 (78% savings)
306
+ * ```
307
+ *
308
+ * @example With routing profile
309
+ * ```ts
310
+ * // Free tier only (zero cost)
311
+ * const result = await client.smartChat('Hello!', { routingProfile: 'free' });
312
+ *
313
+ * // Eco mode (budget optimized)
314
+ * const result = await client.smartChat('Explain quantum computing', { routingProfile: 'eco' });
315
+ *
316
+ * // Premium mode (best quality)
317
+ * const result = await client.smartChat('Write a business plan', { routingProfile: 'premium' });
318
+ * ```
319
+ */
320
+ smartChat(prompt: string, options?: SmartChatOptions): Promise<SmartChatResponse>;
321
+ /**
322
+ * Get model pricing map (cached).
323
+ * Fetches from API on first call, then returns cached result.
324
+ */
325
+ private getModelPricing;
326
+ /**
327
+ * Fetch model pricing from API.
328
+ */
329
+ private fetchModelPricing;
210
330
  /**
211
331
  * Full chat completion interface (OpenAI-compatible).
212
332
  *
@@ -266,7 +386,38 @@ declare class LLMClient {
266
386
  * Get the wallet address being used for payments.
267
387
  */
268
388
  getWalletAddress(): string;
389
+ /**
390
+ * Check if client is configured for testnet.
391
+ */
392
+ isTestnet(): boolean;
269
393
  }
394
+ /**
395
+ * Create a testnet LLM client for development and testing.
396
+ *
397
+ * This is a convenience function that creates an LLMClient configured
398
+ * for the BlockRun testnet (Base Sepolia).
399
+ *
400
+ * @param options - Client options (privateKey required unless BASE_CHAIN_WALLET_KEY env var is set)
401
+ * @returns LLMClient configured for testnet
402
+ *
403
+ * @example
404
+ * ```ts
405
+ * import { testnetClient } from '@blockrun/llm';
406
+ *
407
+ * const client = testnetClient({ privateKey: '0x...' });
408
+ * const response = await client.chat('openai/gpt-oss-20b', 'Hello!');
409
+ * ```
410
+ *
411
+ * Testnet Setup:
412
+ * 1. Get testnet ETH from https://www.alchemy.com/faucets/base-sepolia
413
+ * 2. Get testnet USDC from https://faucet.circle.com/
414
+ * 3. Use your wallet with testnet funds
415
+ *
416
+ * Available Testnet Models:
417
+ * - openai/gpt-oss-20b
418
+ * - openai/gpt-oss-120b
419
+ */
420
+ declare function testnetClient(options?: Omit<LLMClientOptions, 'apiUrl'>): LLMClient;
270
421
 
271
422
  /**
272
423
  * BlockRun Image Client - Generate images via x402 micropayments.
@@ -485,8 +636,11 @@ interface OpenAIClientOptions {
485
636
  interface OpenAIChatCompletionParams {
486
637
  model: string;
487
638
  messages: Array<{
488
- role: "system" | "user" | "assistant";
489
- content: string;
639
+ role: "system" | "user" | "assistant" | "tool";
640
+ content?: string | null;
641
+ name?: string;
642
+ tool_call_id?: string;
643
+ tool_calls?: ToolCall[];
490
644
  }>;
491
645
  max_tokens?: number;
492
646
  temperature?: number;
@@ -497,14 +651,17 @@ interface OpenAIChatCompletionParams {
497
651
  presence_penalty?: number;
498
652
  frequency_penalty?: number;
499
653
  user?: string;
654
+ tools?: Tool[];
655
+ tool_choice?: ToolChoice;
500
656
  }
501
657
  interface OpenAIChatCompletionChoice {
502
658
  index: number;
503
659
  message: {
504
660
  role: "assistant";
505
- content: string;
661
+ content?: string | null;
662
+ tool_calls?: ToolCall[];
506
663
  };
507
- finish_reason: string | null;
664
+ finish_reason: "stop" | "length" | "content_filter" | "tool_calls" | null;
508
665
  }
509
666
  interface OpenAIChatCompletionResponse {
510
667
  id: string;
@@ -584,4 +741,4 @@ declare class OpenAI {
584
741
  getWalletAddress(): string;
585
742
  }
586
743
 
587
- export { APIError, BASE_CHAIN_ID, BlockrunError, type ChatChoice, type ChatCompletionOptions, type ChatMessage, type ChatOptions, type ChatResponse, type ChatUsage, ImageClient, type ImageClientOptions, type ImageData, type ImageGenerateOptions, type ImageModel, type ImageResponse, LLMClient, type LLMClientOptions, type Model, type NewsSearchSource, OpenAI, type OpenAIChatCompletionChoice, type OpenAIChatCompletionChunk, type OpenAIChatCompletionParams, type OpenAIChatCompletionResponse, type OpenAIClientOptions, PaymentError, type PaymentLinks, type RssSearchSource, type SearchParameters, type SearchSource, type Spending, USDC_BASE, USDC_BASE_CONTRACT, WALLET_DIR_PATH, WALLET_FILE_PATH, type WalletInfo, type WebSearchSource, type XSearchSource, createWallet, LLMClient as default, formatFundingMessageCompact, formatNeedsFundingMessage, formatWalletCreatedMessage, getEip681Uri, getOrCreateWallet, getPaymentLinks, getWalletAddress, loadWallet, saveWallet };
744
+ export { APIError, BASE_CHAIN_ID, BlockrunError, type ChatChoice, type ChatCompletionOptions, type ChatMessage, type ChatOptions, type ChatResponse, type ChatUsage, type FunctionCall, type FunctionDefinition, ImageClient, type ImageClientOptions, type ImageData, type ImageGenerateOptions, type ImageModel, type ImageResponse, LLMClient, type LLMClientOptions, type Model, type NewsSearchSource, OpenAI, type OpenAIChatCompletionChoice, type OpenAIChatCompletionChunk, type OpenAIChatCompletionParams, type OpenAIChatCompletionResponse, type OpenAIClientOptions, PaymentError, type PaymentLinks, type RoutingDecision, type RoutingProfile, type RoutingTier, type RssSearchSource, type SearchParameters, type SearchSource, type SmartChatOptions, type SmartChatResponse, type Spending, type Tool, type ToolCall, type ToolChoice, USDC_BASE, USDC_BASE_CONTRACT, WALLET_DIR_PATH, WALLET_FILE_PATH, type WalletInfo, type WebSearchSource, type XSearchSource, createWallet, LLMClient as default, formatFundingMessageCompact, formatNeedsFundingMessage, formatWalletCreatedMessage, getEip681Uri, getOrCreateWallet, getPaymentLinks, getWalletAddress, loadWallet, saveWallet, testnetClient };
package/dist/index.js CHANGED
@@ -27,6 +27,9 @@ var APIError = class extends BlockrunError {
27
27
  }
28
28
  };
29
29
 
30
+ // src/client.ts
31
+ import { route, DEFAULT_ROUTING_CONFIG } from "@blockrun/clawrouter";
32
+
30
33
  // src/x402.ts
31
34
  import { signTypedData } from "viem/accounts";
32
35
  var BASE_CHAIN_ID = 8453;
@@ -223,15 +226,22 @@ function validateResourceUrl(url, baseUrl) {
223
226
 
224
227
  // src/client.ts
225
228
  var DEFAULT_API_URL = "https://blockrun.ai/api";
229
+ var TESTNET_API_URL = "https://testnet.blockrun.ai/api";
226
230
  var DEFAULT_MAX_TOKENS = 1024;
227
231
  var DEFAULT_TIMEOUT = 6e4;
232
+ var SDK_VERSION = "0.3.0";
233
+ var USER_AGENT = `blockrun-ts/${SDK_VERSION}`;
228
234
  var LLMClient = class {
235
+ static DEFAULT_API_URL = DEFAULT_API_URL;
236
+ static TESTNET_API_URL = TESTNET_API_URL;
229
237
  account;
230
238
  privateKey;
231
239
  apiUrl;
232
240
  timeout;
233
241
  sessionTotalUsd = 0;
234
242
  sessionCalls = 0;
243
+ modelPricingCache = null;
244
+ modelPricingPromise = null;
235
245
  /**
236
246
  * Initialize the BlockRun LLM client.
237
247
  *
@@ -278,7 +288,92 @@ var LLMClient = class {
278
288
  search: options?.search,
279
289
  searchParameters: options?.searchParameters
280
290
  });
281
- return result.choices[0].message.content;
291
+ return result.choices[0].message.content || "";
292
+ }
293
+ /**
294
+ * Smart chat with automatic model routing.
295
+ *
296
+ * Uses ClawRouter's 14-dimension rule-based scoring algorithm (<1ms, 100% local)
297
+ * to select the cheapest model that can handle your request.
298
+ *
299
+ * @param prompt - User message
300
+ * @param options - Optional chat and routing parameters
301
+ * @returns SmartChatResponse with response text, selected model, and routing metadata
302
+ *
303
+ * @example Simple usage (auto profile)
304
+ * ```ts
305
+ * const result = await client.smartChat('What is 2+2?');
306
+ * console.log(result.response); // '4'
307
+ * console.log(result.model); // 'google/gemini-2.5-flash'
308
+ * console.log(result.routing.savings); // 0.78 (78% savings)
309
+ * ```
310
+ *
311
+ * @example With routing profile
312
+ * ```ts
313
+ * // Free tier only (zero cost)
314
+ * const result = await client.smartChat('Hello!', { routingProfile: 'free' });
315
+ *
316
+ * // Eco mode (budget optimized)
317
+ * const result = await client.smartChat('Explain quantum computing', { routingProfile: 'eco' });
318
+ *
319
+ * // Premium mode (best quality)
320
+ * const result = await client.smartChat('Write a business plan', { routingProfile: 'premium' });
321
+ * ```
322
+ */
323
+ async smartChat(prompt, options) {
324
+ const modelPricing = await this.getModelPricing();
325
+ const maxOutputTokens = options?.maxOutputTokens || options?.maxTokens || 1024;
326
+ const decision = route(prompt, options?.system, maxOutputTokens, {
327
+ config: DEFAULT_ROUTING_CONFIG,
328
+ modelPricing,
329
+ routingProfile: options?.routingProfile
330
+ });
331
+ const response = await this.chat(decision.model, prompt, {
332
+ system: options?.system,
333
+ maxTokens: options?.maxTokens,
334
+ temperature: options?.temperature,
335
+ topP: options?.topP,
336
+ search: options?.search,
337
+ searchParameters: options?.searchParameters
338
+ });
339
+ return {
340
+ response,
341
+ model: decision.model,
342
+ routing: decision
343
+ };
344
+ }
345
+ /**
346
+ * Get model pricing map (cached).
347
+ * Fetches from API on first call, then returns cached result.
348
+ */
349
+ async getModelPricing() {
350
+ if (this.modelPricingCache) {
351
+ return this.modelPricingCache;
352
+ }
353
+ if (this.modelPricingPromise) {
354
+ return this.modelPricingPromise;
355
+ }
356
+ this.modelPricingPromise = this.fetchModelPricing();
357
+ try {
358
+ this.modelPricingCache = await this.modelPricingPromise;
359
+ return this.modelPricingCache;
360
+ } finally {
361
+ this.modelPricingPromise = null;
362
+ }
363
+ }
364
+ /**
365
+ * Fetch model pricing from API.
366
+ */
367
+ async fetchModelPricing() {
368
+ const models = await this.listModels();
369
+ const pricing = /* @__PURE__ */ new Map();
370
+ for (const model of models) {
371
+ pricing.set(model.id, {
372
+ inputPrice: model.inputPrice,
373
+ outputPrice: model.outputPrice
374
+ });
375
+ }
376
+ return pricing;
282
377
  }
283
378
  /**
284
379
  * Full chat completion interface (OpenAI-compatible).
@@ -305,6 +400,12 @@ var LLMClient = class {
305
400
  } else if (options?.search === true) {
306
401
  body.search_parameters = { mode: "on" };
307
402
  }
403
+ if (options?.tools !== void 0) {
404
+ body.tools = options.tools;
405
+ }
406
+ if (options?.toolChoice !== void 0) {
407
+ body.tool_choice = options.toolChoice;
408
+ }
308
409
  return this.requestWithPayment("/v1/chat/completions", body);
309
410
  }
310
411
  /**
@@ -314,7 +415,7 @@ var LLMClient = class {
314
415
  const url = `${this.apiUrl}${endpoint}`;
315
416
  const response = await this.fetchWithTimeout(url, {
316
417
  method: "POST",
317
- headers: { "Content-Type": "application/json" },
418
+ headers: { "Content-Type": "application/json", "User-Agent": USER_AGENT },
318
419
  body: JSON.stringify(body)
319
420
  });
320
421
  if (response.status === 402) {
@@ -377,6 +478,7 @@ var LLMClient = class {
377
478
  method: "POST",
378
479
  headers: {
379
480
  "Content-Type": "application/json",
481
+ "User-Agent": USER_AGENT,
380
482
  "PAYMENT-SIGNATURE": paymentPayload
381
483
  },
382
484
  body: JSON.stringify(body)
@@ -505,7 +607,19 @@ var LLMClient = class {
505
607
  getWalletAddress() {
506
608
  return this.account.address;
507
609
  }
610
+ /**
611
+ * Check if client is configured for testnet.
612
+ */
613
+ isTestnet() {
614
+ return this.apiUrl.includes("testnet.blockrun.ai");
615
+ }
508
616
  };
617
+ function testnetClient(options = {}) {
618
+ return new LLMClient({
619
+ ...options,
620
+ apiUrl: TESTNET_API_URL
621
+ });
622
+ }
509
623
  var client_default = LLMClient;
510
624
 
511
625
  // src/image.ts
@@ -907,7 +1021,9 @@ var ChatCompletions = class {
907
1021
  {
908
1022
  maxTokens: params.max_tokens,
909
1023
  temperature: params.temperature,
910
- topP: params.top_p
1024
+ topP: params.top_p,
1025
+ tools: params.tools,
1026
+ toolChoice: params.tool_choice
911
1027
  }
912
1028
  );
913
1029
  return this.transformResponse(response);
@@ -922,6 +1038,12 @@ var ChatCompletions = class {
922
1038
  top_p: params.top_p,
923
1039
  stream: true
924
1040
  };
1041
+ if (params.tools) {
1042
+ body.tools = params.tools;
1043
+ }
1044
+ if (params.tool_choice) {
1045
+ body.tool_choice = params.tool_choice;
1046
+ }
925
1047
  const controller = new AbortController();
926
1048
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
927
1049
  try {
@@ -958,7 +1080,8 @@ var ChatCompletions = class {
958
1080
  index: choice.index ?? index,
959
1081
  message: {
960
1082
  role: "assistant",
961
- content: choice.message.content
1083
+ content: choice.message.content,
1084
+ tool_calls: choice.message.tool_calls
962
1085
  },
963
1086
  finish_reason: choice.finish_reason || "stop"
964
1087
  })),
@@ -1015,5 +1138,6 @@ export {
1015
1138
  getPaymentLinks,
1016
1139
  getWalletAddress,
1017
1140
  loadWallet,
1018
- saveWallet
1141
+ saveWallet,
1142
+ testnetClient
1019
1143
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/llm",
3
- "version": "0.2.1",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
5
  "description": "BlockRun LLM Gateway SDK - Pay-per-request AI via x402 on Base",
6
6
  "main": "dist/index.cjs",
@@ -48,6 +48,7 @@
48
48
  "url": "https://github.com/BlockRunAI/blockrun-llm-ts/issues"
49
49
  },
50
50
  "dependencies": {
51
+ "@blockrun/clawrouter": "^0.9.3",
51
52
  "viem": "^2.21.0"
52
53
  },
53
54
  "optionalDependencies": {