@blockrun/llm 3.8.2 → 3.8.4
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 +8 -8
- package/dist/index.cjs +79 -30
- package/dist/index.d.cts +19 -30
- package/dist/index.d.ts +19 -30
- package/dist/index.js +79 -29
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @blockrun/llm (TypeScript SDK)
|
|
2
2
|
|
|
3
|
-
> **@blockrun/llm** is a TypeScript/Node.js SDK for accessing
|
|
3
|
+
> **@blockrun/llm** is a TypeScript/Node.js SDK for accessing <!-- br:models.chatVisible -->66<!-- /br:models.chatVisible --> large language models (GPT-5, Claude, Gemini, Grok, DeepSeek, Kimi, and more) with automatic pay-per-request USDC micropayments via the x402 protocol. No API keys required — your wallet signature is your authentication. Supports **streaming**, smart routing, Base and Solana chains.
|
|
4
4
|
>
|
|
5
5
|
> 🆓 **Includes 7 fully-free NVIDIA-hosted models** (5 visible in `/v1/models`, 2 hidden but directly callable) — DeepSeek V4 Flash (1M context), Nemotron Nano Omni (vision), Qwen3 Coder, Llama 4, Mistral, plus the gpt-oss pair. Zero USDC, no rate-limit gimmicks. Use `routingProfile: 'free'` or call any `nvidia/*` model directly.
|
|
6
6
|
|
|
@@ -232,7 +232,7 @@ const client = new LLMClient();
|
|
|
232
232
|
const result = await client.smartChat('What is 2+2?');
|
|
233
233
|
console.log(result.response); // '4'
|
|
234
234
|
console.log(result.model); // 'moonshot/kimi-k2.5' (cheap, fast)
|
|
235
|
-
console.log(`Saved ${(result.routing.savings * 100).toFixed(0)}%`); // 'Saved
|
|
235
|
+
console.log(`Saved ${(result.routing.savings * 100).toFixed(0)}%`); // 'Saved 87%'
|
|
236
236
|
|
|
237
237
|
// Complex reasoning task -> routes to reasoning model
|
|
238
238
|
const complex = await client.smartChat('Prove the Riemann hypothesis step by step');
|
|
@@ -263,7 +263,7 @@ const reply = await client.chat('nvidia/deepseek-v4-flash', 'hello', {
|
|
|
263
263
|
|
|
264
264
|
| Profile | Description | Best For |
|
|
265
265
|
|---------|-------------|----------|
|
|
266
|
-
| `free` | NVIDIA free tier — smart-routes across 8 models (DeepSeek V4 Flash, Nemotron Nano Omni, Qwen3, Llama 4, Mistral, plus 2 hidden gpt-oss) | Zero-cost testing, dev, prod |
|
|
266
|
+
| `free` | NVIDIA free tier — smart-routes across <!-- br:models.free -->8<!-- /br:models.free --> models (DeepSeek V4 Flash, Nemotron Nano Omni, Qwen3, Llama 4, Mistral, plus 2 hidden gpt-oss) | Zero-cost testing, dev, prod |
|
|
267
267
|
| `eco` | Cheapest models per tier (DeepSeek, xAI) | Cost-sensitive production |
|
|
268
268
|
| `auto` | Best balance of cost/quality (default) | General use |
|
|
269
269
|
| `premium` | Top-tier models (OpenAI, Anthropic) | Quality-critical tasks |
|
|
@@ -748,7 +748,7 @@ Generic escape hatches: `client.defi(path, params)`, `client.dex(path, params, b
|
|
|
748
748
|
### Multi-chain RPC
|
|
749
749
|
|
|
750
750
|
`RpcClient` wraps `POST /v1/rpc/{network}` — standard JSON-RPC 2.0 access to
|
|
751
|
-
40
|
|
751
|
+
<!-- br:chains.rpc -->40<!-- /br:chains.rpc --> chains through one endpoint (Ethereum, Base, Solana, Polygon, BSC,
|
|
752
752
|
Arbitrum, Optimism, Avalanche, Bitcoin, Sui, and more; powered by Tatum's RPC
|
|
753
753
|
gateway). No API key, no per-chain endpoints: flat **$0.002 per call** in
|
|
754
754
|
USDC; a JSON-RPC batch charges per element.
|
|
@@ -858,7 +858,7 @@ const response2 = await client.chat('anthropic/claude-sonnet-4', 'Write a haiku'
|
|
|
858
858
|
|
|
859
859
|
### Smart Routing (ClawRouter)
|
|
860
860
|
|
|
861
|
-
Save up to
|
|
861
|
+
Save up to <!-- br:savings.autoVsBaselinePct -->87<!-- /br:savings.autoVsBaselinePct -->% on inference costs with intelligent model routing. ClawRouter uses a <!-- br:clawrouter.dimensions -->15<!-- /br:clawrouter.dimensions -->-dimension rule-based scoring algorithm to select the cheapest model that can handle your request (<1ms, 100% local).
|
|
862
862
|
|
|
863
863
|
```typescript
|
|
864
864
|
import { LLMClient } from '@blockrun/llm';
|
|
@@ -870,7 +870,7 @@ const result = await client.smartChat('What is 2+2?');
|
|
|
870
870
|
console.log(result.response); // '4'
|
|
871
871
|
console.log(result.model); // 'google/gemini-2.5-flash'
|
|
872
872
|
console.log(result.routing.tier); // 'SIMPLE'
|
|
873
|
-
console.log(`Saved ${(result.routing.savings * 100).toFixed(0)}%`); // 'Saved
|
|
873
|
+
console.log(`Saved ${(result.routing.savings * 100).toFixed(0)}%`); // 'Saved 87%'
|
|
874
874
|
|
|
875
875
|
// Routing profiles
|
|
876
876
|
const free = await client.smartChat('Hello!', { routingProfile: 'free' }); // Zero cost
|
|
@@ -883,7 +883,7 @@ const premium = await client.smartChat('Write a legal brief', { routingProfile:
|
|
|
883
883
|
|
|
884
884
|
| Profile | Description | Best For |
|
|
885
885
|
|---------|-------------|----------|
|
|
886
|
-
| `free` | NVIDIA free tier (
|
|
886
|
+
| `free` | NVIDIA free tier (<!-- br:models.free -->8<!-- /br:models.free --> models, smart-routed) | Zero-cost testing, dev, prod |
|
|
887
887
|
| `eco` | Budget-optimized | Cost-sensitive workloads |
|
|
888
888
|
| `auto` | Intelligent routing (default) | General use |
|
|
889
889
|
| `premium` | Best quality models | Critical tasks |
|
|
@@ -1427,7 +1427,7 @@ The `AnthropicClient` wraps the official `@anthropic-ai/sdk` with a custom fetch
|
|
|
1427
1427
|
When you make an API call, the SDK automatically handles x402 payment. It signs a USDC transaction locally using your wallet private key (which never leaves your machine), and includes the payment proof in the request header. Settlement is non-custodial and instant on Base or Solana.
|
|
1428
1428
|
|
|
1429
1429
|
### What is smart routing / ClawRouter?
|
|
1430
|
-
ClawRouter is a built-in smart routing engine that analyzes your request across
|
|
1430
|
+
ClawRouter is a built-in smart routing engine that analyzes your request across <!-- br:clawrouter.dimensions -->15<!-- /br:clawrouter.dimensions --> dimensions and automatically picks the cheapest model capable of handling it. Routing happens locally in under 1ms. It can save up to <!-- br:savings.autoVsBaselinePct -->87<!-- /br:savings.autoVsBaselinePct -->% on LLM costs compared to using premium models for every request.
|
|
1431
1431
|
|
|
1432
1432
|
### Does it support streaming?
|
|
1433
1433
|
Yes — as of v1.6.1. Use `client.chatCompletionStream()` for native streaming or `stream: true` in the OpenAI-compatible client. Payment is handled automatically: the SDK signs USDC payment before streaming begins, and caches payment requirements per model so subsequent calls skip the 402 round-trip (~200ms faster).
|
package/dist/index.cjs
CHANGED
|
@@ -38,7 +38,6 @@ __export(index_exports, {
|
|
|
38
38
|
ImageClient: () => ImageClient,
|
|
39
39
|
KNOWN_PROVIDERS: () => KNOWN_PROVIDERS,
|
|
40
40
|
LLMClient: () => LLMClient,
|
|
41
|
-
MAX_TOKENS_SANITY_LIMIT: () => MAX_TOKENS_SANITY_LIMIT,
|
|
42
41
|
MusicClient: () => MusicClient,
|
|
43
42
|
NETWORK_ALIASES: () => NETWORK_ALIASES,
|
|
44
43
|
OpenAI: () => OpenAI,
|
|
@@ -145,8 +144,47 @@ var BASE_CHAIN_ID = 8453;
|
|
|
145
144
|
var USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
146
145
|
var SOLANA_NETWORK = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
|
|
147
146
|
var USDC_SOLANA = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
147
|
+
var SOLANA_USDC_DECIMALS = 6;
|
|
148
148
|
var DEFAULT_COMPUTE_UNIT_PRICE_MICROLAMPORTS = 1;
|
|
149
149
|
var DEFAULT_COMPUTE_UNIT_LIMIT = 8e3;
|
|
150
|
+
var BLOCKHASH_TTL_MS = 1e4;
|
|
151
|
+
var MAX_FEE_NONCE_STEPS = 64;
|
|
152
|
+
var MAX_TRACKED_BLOCKHASHES = 8;
|
|
153
|
+
var MAX_TRACKED_ENDPOINTS = 8;
|
|
154
|
+
var blockhashCache = /* @__PURE__ */ new Map();
|
|
155
|
+
function trimOldest(map, limit) {
|
|
156
|
+
while (map.size > limit) {
|
|
157
|
+
const oldest = map.keys().next().value;
|
|
158
|
+
map.delete(oldest);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
var issuedByBlockhash = /* @__PURE__ */ new Map();
|
|
162
|
+
function issuedFor(blockhash) {
|
|
163
|
+
let issued = issuedByBlockhash.get(blockhash);
|
|
164
|
+
if (!issued) {
|
|
165
|
+
issued = /* @__PURE__ */ new Set();
|
|
166
|
+
issuedByBlockhash.set(blockhash, issued);
|
|
167
|
+
trimOldest(issuedByBlockhash, MAX_TRACKED_BLOCKHASHES);
|
|
168
|
+
}
|
|
169
|
+
return issued;
|
|
170
|
+
}
|
|
171
|
+
async function getBlockhashEntry(connection, rpcUrl, forceRefresh) {
|
|
172
|
+
const now = Date.now();
|
|
173
|
+
const cached = blockhashCache.get(rpcUrl);
|
|
174
|
+
if (!forceRefresh && cached && now - cached.fetchedAt < BLOCKHASH_TTL_MS) {
|
|
175
|
+
return cached;
|
|
176
|
+
}
|
|
177
|
+
const { blockhash } = await connection.getLatestBlockhash();
|
|
178
|
+
const current = blockhashCache.get(rpcUrl);
|
|
179
|
+
if (current?.blockhash === blockhash) {
|
|
180
|
+
current.fetchedAt = now;
|
|
181
|
+
return current;
|
|
182
|
+
}
|
|
183
|
+
const entry = { blockhash, fetchedAt: now };
|
|
184
|
+
blockhashCache.set(rpcUrl, entry);
|
|
185
|
+
trimOldest(blockhashCache, MAX_TRACKED_ENDPOINTS);
|
|
186
|
+
return entry;
|
|
187
|
+
}
|
|
150
188
|
var USDC_DOMAIN = {
|
|
151
189
|
name: "USD Coin",
|
|
152
190
|
version: "2",
|
|
@@ -231,7 +269,7 @@ async function createPaymentPayload(privateKey, fromAddress, recipient, amount,
|
|
|
231
269
|
}
|
|
232
270
|
async function createSolanaPaymentPayload(secretKey, fromAddress, recipient, amount, feePayer, options = {}) {
|
|
233
271
|
const { Connection, PublicKey, TransactionMessage, VersionedTransaction, ComputeBudgetProgram } = await import("@solana/web3.js");
|
|
234
|
-
const { getAssociatedTokenAddress, createTransferCheckedInstruction
|
|
272
|
+
const { getAssociatedTokenAddress, createTransferCheckedInstruction } = await import("@solana/spl-token");
|
|
235
273
|
const { Keypair } = await import("@solana/web3.js");
|
|
236
274
|
const rpcUrl = options.rpcUrl || "https://sol.blockrun.ai/api/v1/solana/rpc";
|
|
237
275
|
const connection = options.rpcHeaders ? new Connection(rpcUrl, { httpHeaders: options.rpcHeaders }) : new Connection(rpcUrl);
|
|
@@ -240,32 +278,53 @@ async function createSolanaPaymentPayload(secretKey, fromAddress, recipient, amo
|
|
|
240
278
|
const ownerPubkey = keypair.publicKey;
|
|
241
279
|
const tokenMint = new PublicKey(USDC_SOLANA);
|
|
242
280
|
const payToPubkey = new PublicKey(recipient);
|
|
243
|
-
const mintInfo = await getMint(connection, tokenMint);
|
|
244
281
|
const sourceATA = await getAssociatedTokenAddress(tokenMint, ownerPubkey, false);
|
|
245
282
|
const destinationATA = await getAssociatedTokenAddress(tokenMint, payToPubkey, false);
|
|
246
|
-
const { blockhash } = await connection.getLatestBlockhash();
|
|
247
|
-
const setComputeUnitPriceIx = ComputeBudgetProgram.setComputeUnitPrice({
|
|
248
|
-
microLamports: DEFAULT_COMPUTE_UNIT_PRICE_MICROLAMPORTS
|
|
249
|
-
});
|
|
250
|
-
const setComputeUnitLimitIx = ComputeBudgetProgram.setComputeUnitLimit({
|
|
251
|
-
units: DEFAULT_COMPUTE_UNIT_LIMIT
|
|
252
|
-
});
|
|
253
283
|
const transferIx = createTransferCheckedInstruction(
|
|
254
284
|
sourceATA,
|
|
255
285
|
tokenMint,
|
|
256
286
|
destinationATA,
|
|
257
287
|
ownerPubkey,
|
|
258
288
|
BigInt(amount),
|
|
259
|
-
|
|
289
|
+
SOLANA_USDC_DECIMALS
|
|
260
290
|
);
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
291
|
+
const buildSignedTx = (blockhash, unitPriceMicroLamports) => {
|
|
292
|
+
const messageV0 = new TransactionMessage({
|
|
293
|
+
payerKey: feePayerPubkey,
|
|
294
|
+
recentBlockhash: blockhash,
|
|
295
|
+
instructions: [
|
|
296
|
+
ComputeBudgetProgram.setComputeUnitLimit({ units: DEFAULT_COMPUTE_UNIT_LIMIT }),
|
|
297
|
+
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: unitPriceMicroLamports }),
|
|
298
|
+
transferIx
|
|
299
|
+
]
|
|
300
|
+
}).compileToV0Message();
|
|
301
|
+
const transaction = new VersionedTransaction(messageV0);
|
|
302
|
+
transaction.sign([keypair]);
|
|
303
|
+
return Buffer.from(transaction.serialize()).toString("base64");
|
|
304
|
+
};
|
|
305
|
+
const findDistinctTx = (candidate) => {
|
|
306
|
+
const issued = issuedFor(candidate.blockhash);
|
|
307
|
+
for (let step = 0; step <= MAX_FEE_NONCE_STEPS; step++) {
|
|
308
|
+
const tx = buildSignedTx(
|
|
309
|
+
candidate.blockhash,
|
|
310
|
+
DEFAULT_COMPUTE_UNIT_PRICE_MICROLAMPORTS + step
|
|
311
|
+
);
|
|
312
|
+
if (!issued.has(tx)) return tx;
|
|
313
|
+
}
|
|
314
|
+
return null;
|
|
315
|
+
};
|
|
316
|
+
let entry = await getBlockhashEntry(connection, rpcUrl, false);
|
|
317
|
+
let serializedTx = findDistinctTx(entry);
|
|
318
|
+
if (serializedTx === null) {
|
|
319
|
+
entry = await getBlockhashEntry(connection, rpcUrl, true);
|
|
320
|
+
serializedTx = findDistinctTx(entry);
|
|
321
|
+
}
|
|
322
|
+
if (serializedTx === null) {
|
|
323
|
+
throw new Error(
|
|
324
|
+
`Solana payment: ${MAX_FEE_NONCE_STEPS + 1} identical payments already issued against blockhash ${entry.blockhash}. Retry once it rotates.`
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
issuedFor(entry.blockhash).add(serializedTx);
|
|
269
328
|
const paymentData = {
|
|
270
329
|
x402Version: 2,
|
|
271
330
|
resource: {
|
|
@@ -356,7 +415,6 @@ function validateModel(model) {
|
|
|
356
415
|
throw new Error("Model must be a non-empty string");
|
|
357
416
|
}
|
|
358
417
|
}
|
|
359
|
-
var MAX_TOKENS_SANITY_LIMIT = 1e6;
|
|
360
418
|
function validateMaxTokens(maxTokens) {
|
|
361
419
|
if (maxTokens === void 0 || maxTokens === null) {
|
|
362
420
|
return;
|
|
@@ -367,14 +425,6 @@ function validateMaxTokens(maxTokens) {
|
|
|
367
425
|
if (maxTokens < 1) {
|
|
368
426
|
throw new Error("maxTokens must be positive (minimum: 1)");
|
|
369
427
|
}
|
|
370
|
-
if (maxTokens > MAX_TOKENS_SANITY_LIMIT) {
|
|
371
|
-
throw Object.assign(
|
|
372
|
-
new Error(
|
|
373
|
-
`maxTokens implausibly large (client-side sanity limit: ${MAX_TOKENS_SANITY_LIMIT}). This is not a model limit \u2014 the gateway enforces the real per-model ceiling and reports it.`
|
|
374
|
-
),
|
|
375
|
-
{ code: "MAX_TOKENS_SANITY_LIMIT", limit: MAX_TOKENS_SANITY_LIMIT }
|
|
376
|
-
);
|
|
377
|
-
}
|
|
378
428
|
}
|
|
379
429
|
function validateTemperature(temperature) {
|
|
380
430
|
if (temperature === void 0 || temperature === null) {
|
|
@@ -514,7 +564,7 @@ function getCostSummary() {
|
|
|
514
564
|
}
|
|
515
565
|
|
|
516
566
|
// src/version.ts
|
|
517
|
-
var SDK_VERSION = "3.8.
|
|
567
|
+
var SDK_VERSION = "3.8.4";
|
|
518
568
|
var USER_AGENT = `blockrun-ts/${SDK_VERSION}`;
|
|
519
569
|
|
|
520
570
|
// src/client.ts
|
|
@@ -6139,7 +6189,6 @@ var AnthropicClient = class {
|
|
|
6139
6189
|
ImageClient,
|
|
6140
6190
|
KNOWN_PROVIDERS,
|
|
6141
6191
|
LLMClient,
|
|
6142
|
-
MAX_TOKENS_SANITY_LIMIT,
|
|
6143
6192
|
MusicClient,
|
|
6144
6193
|
NETWORK_ALIASES,
|
|
6145
6194
|
OpenAI,
|
package/dist/index.d.cts
CHANGED
|
@@ -3161,37 +3161,26 @@ declare const KNOWN_PROVIDERS: Set<string>;
|
|
|
3161
3161
|
*/
|
|
3162
3162
|
declare function validateModel(model: string): void;
|
|
3163
3163
|
/**
|
|
3164
|
-
*
|
|
3165
|
-
*
|
|
3166
|
-
*
|
|
3167
|
-
*
|
|
3168
|
-
*
|
|
3169
|
-
*
|
|
3170
|
-
*
|
|
3171
|
-
*
|
|
3172
|
-
*
|
|
3173
|
-
*
|
|
3174
|
-
*
|
|
3175
|
-
*
|
|
3176
|
-
*
|
|
3177
|
-
*
|
|
3178
|
-
*
|
|
3179
|
-
*
|
|
3180
|
-
*
|
|
3181
|
-
* as-of-date probe results, not a maintained invariant — nothing in this repo
|
|
3182
|
-
* ships a model catalog, so CI cannot detect when they go stale.
|
|
3183
|
-
*
|
|
3184
|
-
* Keep a bound so an obvious mistake (1e9, a byte count, a timestamp) fails
|
|
3185
|
-
* fast locally instead of becoming a payment quote. Set it far above any real
|
|
3186
|
-
* model so it can never be the binding constraint again.
|
|
3187
|
-
*/
|
|
3188
|
-
declare const MAX_TOKENS_SANITY_LIMIT = 1000000;
|
|
3189
|
-
/**
|
|
3190
|
-
* Validates that max_tokens is a positive integer no larger than
|
|
3191
|
-
* {@link MAX_TOKENS_SANITY_LIMIT}.
|
|
3164
|
+
* Mirrors the gateway's own contract for `max_tokens`, which is exactly
|
|
3165
|
+
* `z.number().int().min(1).optional()` — an integer, at least 1, and
|
|
3166
|
+
* deliberately **no upper bound**.
|
|
3167
|
+
*
|
|
3168
|
+
* There is no ceiling here on purpose. The gateway clamps every request with
|
|
3169
|
+
* `min(requested, model.maxOutput, contextHeadroom)` and then quotes only a
|
|
3170
|
+
* fraction of the clamped value, so an oversized `maxTokens` cannot inflate a
|
|
3171
|
+
* payment: 1e9 and 1_000_000 produce a byte-identical quote, because both land
|
|
3172
|
+
* on the same `model.maxOutput`. A client-side ceiling could therefore only
|
|
3173
|
+
* ever reject a request the gateway would have accepted and handled correctly.
|
|
3174
|
+
*
|
|
3175
|
+
* Earlier versions of this file did carry one, justified in a comment as
|
|
3176
|
+
* stopping a stray 1e9 from "becoming a payment quote". That was never true —
|
|
3177
|
+
* it was asserted here without being checked against the route handler, which
|
|
3178
|
+
* is the same mistake as the old `"maxTokens too large (maximum: 100000)"`
|
|
3179
|
+
* message that got recorded downstream as an upstream model ceiling. Verify
|
|
3180
|
+
* against the gateway before reintroducing any number in this file.
|
|
3192
3181
|
*
|
|
3193
3182
|
* @param maxTokens - Maximum number of tokens to generate
|
|
3194
|
-
* @throws {Error} If maxTokens is
|
|
3183
|
+
* @throws {Error} If maxTokens is not a positive integer
|
|
3195
3184
|
*
|
|
3196
3185
|
* @example
|
|
3197
3186
|
* validateMaxTokens(1000);
|
|
@@ -3218,4 +3207,4 @@ declare function validateTemperature(temperature?: number): void;
|
|
|
3218
3207
|
*/
|
|
3219
3208
|
declare function validateTopP(topP?: number): void;
|
|
3220
3209
|
|
|
3221
|
-
export { APIError, AnthropicClient, type AudioModel, type AudioTrack, BASE_CHAIN_ID, type BarResolution, type BlockRunAnthropicOptions, BlockrunClient, type BlockrunClientOptions, BlockrunError, type CallInitiatedResponse, type CallModel, type CallOptions, type CallStatusResponse, type ChatChoice, type ChatCompletionOptions, type ChatMessage, type ChatOptions, type ChatResponse, type ChatResponseWithCost, type ChatUsage, type CostEntry, type CostEstimate, type CreatePaymentOptions, type FunctionCall, type FunctionDefinition, type HistoryOptions, ImageClient, type ImageClientOptions, type ImageData, type ImageEditOptions, type ImageGenerateOptions, type ImageModel, type ImageResponse, KNOWN_PROVIDERS, LLMClient, type LLMClientOptions, type ListOptions,
|
|
3210
|
+
export { APIError, AnthropicClient, type AudioModel, type AudioTrack, BASE_CHAIN_ID, type BarResolution, type BlockRunAnthropicOptions, BlockrunClient, type BlockrunClientOptions, BlockrunError, type CallInitiatedResponse, type CallModel, type CallOptions, type CallStatusResponse, type ChatChoice, type ChatCompletionOptions, type ChatMessage, type ChatOptions, type ChatResponse, type ChatResponseWithCost, type ChatUsage, type CostEntry, type CostEstimate, type CreatePaymentOptions, type FunctionCall, type FunctionDefinition, type HistoryOptions, ImageClient, type ImageClientOptions, type ImageData, type ImageEditOptions, type ImageGenerateOptions, type ImageModel, type ImageResponse, KNOWN_PROVIDERS, LLMClient, type LLMClientOptions, type ListOptions, type MarketSession, type Model, MusicClient, type MusicClientOptions, type MusicGenerateOptions, type MusicResponse, NETWORK_ALIASES, type NewsSearchSource, OpenAI, type OpenAIChatCompletionChoice, type OpenAIChatCompletionChunk, type OpenAIChatCompletionParams, type OpenAIChatCompletionResponse, type OpenAIClientOptions, PHONE_PRICES, PORTRAIT_ENROLLMENT_PRICE_USD, PaymentError, type PaymentLinks, type PhoneBuyOptions, type PhoneBuyResponse, PhoneClient, type PhoneClientOptions, type PhoneListResponse, type PhoneLookupResponse, type PhoneNumberRecord, type PhoneReleaseResponse, type PhoneRenewResponse, type PollOptions, PortraitClient, type PortraitClientOptions, type PortraitEnrollOptions, type PortraitEnrollResponse, type PriceBar, type PriceCategory, PriceClient, type PriceClientOptions, type PriceHistoryResponse, type PriceOptions, type PricePoint, RPC_PRICE_USD, type ResponseFormat, type RoutingDecision, type RoutingProfile, type RoutingTier, type RpcBatchRequest, RpcClient, type RpcClientOptions, type RpcError, type RpcNetwork, type RpcResponse, type RssSearchSource, SOLANA_NETWORK, SOLANA_WALLET_FILE as SOLANA_WALLET_FILE_PATH, SUPPORTED_NETWORKS, SearchClient, type SearchClientOptions, type SearchOptions, type SearchParameters, type SearchResult, type SearchSource, type SearchUsage, type SmartChatOptions, type SmartChatResponse, SolanaLLMClient, type SolanaLLMClientOptions, type SolanaWalletInfo, type SoundEffectOptions, type SpeechAudio, SpeechClient, type SpeechClientOptions, type SpeechGenerateOptions, type SpeechModel, type SpeechResponse, type SpeechVoice, type Spending, type SpendingReport, type StockMarket, SurfClient, type SurfClientOptions, type SymbolListResponse, type Tool, type ToolCall, type ToolChoice, USDC_BASE, USDC_BASE_CONTRACT, USDC_SOLANA, VideoClient, type VideoClientOptions, type VideoClip, type VideoGenerateOptions, type VideoModel, type VideoResponse, VoiceClient, type VoiceClientOptions, type VoiceInfo, type VoicePreset, WALLET_DIR_PATH, WALLET_FILE_PATH, type WalletInfo, type WebSearchSource, type XSearchSource, clearCache, createPaymentPayload, createSolanaPaymentPayload, createSolanaWallet, createWallet, LLMClient as default, extractPaymentDetails, formatFundingMessageCompact, formatNeedsFundingMessage, formatSolanaWalletMigrationNotice, formatWalletCreatedMessage, formatWalletMigrationNotice, getCached, getCachedByRequest, getCostLogSummary, getCostSummary, getEip681Uri, getOrCreateSolanaWallet, getOrCreateWallet, getPaymentLinks, getWalletAddress, importSolanaWallet, importWallet, listDiscoveredSolanaWallets, listDiscoveredWallets, loadSolanaWallet, loadWallet, logCost, parsePaymentRequired, saveSolanaWallet, saveToCache, saveWallet, scanSolanaWallets, scanWallets, setCache, setupAgentSolanaWallet, setupAgentWallet, solanaClient, solanaKeyToBytes, solanaPublicKey, status, validateMaxTokens, validateModel, validateTemperature, validateTopP };
|
package/dist/index.d.ts
CHANGED
|
@@ -3161,37 +3161,26 @@ declare const KNOWN_PROVIDERS: Set<string>;
|
|
|
3161
3161
|
*/
|
|
3162
3162
|
declare function validateModel(model: string): void;
|
|
3163
3163
|
/**
|
|
3164
|
-
*
|
|
3165
|
-
*
|
|
3166
|
-
*
|
|
3167
|
-
*
|
|
3168
|
-
*
|
|
3169
|
-
*
|
|
3170
|
-
*
|
|
3171
|
-
*
|
|
3172
|
-
*
|
|
3173
|
-
*
|
|
3174
|
-
*
|
|
3175
|
-
*
|
|
3176
|
-
*
|
|
3177
|
-
*
|
|
3178
|
-
*
|
|
3179
|
-
*
|
|
3180
|
-
*
|
|
3181
|
-
* as-of-date probe results, not a maintained invariant — nothing in this repo
|
|
3182
|
-
* ships a model catalog, so CI cannot detect when they go stale.
|
|
3183
|
-
*
|
|
3184
|
-
* Keep a bound so an obvious mistake (1e9, a byte count, a timestamp) fails
|
|
3185
|
-
* fast locally instead of becoming a payment quote. Set it far above any real
|
|
3186
|
-
* model so it can never be the binding constraint again.
|
|
3187
|
-
*/
|
|
3188
|
-
declare const MAX_TOKENS_SANITY_LIMIT = 1000000;
|
|
3189
|
-
/**
|
|
3190
|
-
* Validates that max_tokens is a positive integer no larger than
|
|
3191
|
-
* {@link MAX_TOKENS_SANITY_LIMIT}.
|
|
3164
|
+
* Mirrors the gateway's own contract for `max_tokens`, which is exactly
|
|
3165
|
+
* `z.number().int().min(1).optional()` — an integer, at least 1, and
|
|
3166
|
+
* deliberately **no upper bound**.
|
|
3167
|
+
*
|
|
3168
|
+
* There is no ceiling here on purpose. The gateway clamps every request with
|
|
3169
|
+
* `min(requested, model.maxOutput, contextHeadroom)` and then quotes only a
|
|
3170
|
+
* fraction of the clamped value, so an oversized `maxTokens` cannot inflate a
|
|
3171
|
+
* payment: 1e9 and 1_000_000 produce a byte-identical quote, because both land
|
|
3172
|
+
* on the same `model.maxOutput`. A client-side ceiling could therefore only
|
|
3173
|
+
* ever reject a request the gateway would have accepted and handled correctly.
|
|
3174
|
+
*
|
|
3175
|
+
* Earlier versions of this file did carry one, justified in a comment as
|
|
3176
|
+
* stopping a stray 1e9 from "becoming a payment quote". That was never true —
|
|
3177
|
+
* it was asserted here without being checked against the route handler, which
|
|
3178
|
+
* is the same mistake as the old `"maxTokens too large (maximum: 100000)"`
|
|
3179
|
+
* message that got recorded downstream as an upstream model ceiling. Verify
|
|
3180
|
+
* against the gateway before reintroducing any number in this file.
|
|
3192
3181
|
*
|
|
3193
3182
|
* @param maxTokens - Maximum number of tokens to generate
|
|
3194
|
-
* @throws {Error} If maxTokens is
|
|
3183
|
+
* @throws {Error} If maxTokens is not a positive integer
|
|
3195
3184
|
*
|
|
3196
3185
|
* @example
|
|
3197
3186
|
* validateMaxTokens(1000);
|
|
@@ -3218,4 +3207,4 @@ declare function validateTemperature(temperature?: number): void;
|
|
|
3218
3207
|
*/
|
|
3219
3208
|
declare function validateTopP(topP?: number): void;
|
|
3220
3209
|
|
|
3221
|
-
export { APIError, AnthropicClient, type AudioModel, type AudioTrack, BASE_CHAIN_ID, type BarResolution, type BlockRunAnthropicOptions, BlockrunClient, type BlockrunClientOptions, BlockrunError, type CallInitiatedResponse, type CallModel, type CallOptions, type CallStatusResponse, type ChatChoice, type ChatCompletionOptions, type ChatMessage, type ChatOptions, type ChatResponse, type ChatResponseWithCost, type ChatUsage, type CostEntry, type CostEstimate, type CreatePaymentOptions, type FunctionCall, type FunctionDefinition, type HistoryOptions, ImageClient, type ImageClientOptions, type ImageData, type ImageEditOptions, type ImageGenerateOptions, type ImageModel, type ImageResponse, KNOWN_PROVIDERS, LLMClient, type LLMClientOptions, type ListOptions,
|
|
3210
|
+
export { APIError, AnthropicClient, type AudioModel, type AudioTrack, BASE_CHAIN_ID, type BarResolution, type BlockRunAnthropicOptions, BlockrunClient, type BlockrunClientOptions, BlockrunError, type CallInitiatedResponse, type CallModel, type CallOptions, type CallStatusResponse, type ChatChoice, type ChatCompletionOptions, type ChatMessage, type ChatOptions, type ChatResponse, type ChatResponseWithCost, type ChatUsage, type CostEntry, type CostEstimate, type CreatePaymentOptions, type FunctionCall, type FunctionDefinition, type HistoryOptions, ImageClient, type ImageClientOptions, type ImageData, type ImageEditOptions, type ImageGenerateOptions, type ImageModel, type ImageResponse, KNOWN_PROVIDERS, LLMClient, type LLMClientOptions, type ListOptions, type MarketSession, type Model, MusicClient, type MusicClientOptions, type MusicGenerateOptions, type MusicResponse, NETWORK_ALIASES, type NewsSearchSource, OpenAI, type OpenAIChatCompletionChoice, type OpenAIChatCompletionChunk, type OpenAIChatCompletionParams, type OpenAIChatCompletionResponse, type OpenAIClientOptions, PHONE_PRICES, PORTRAIT_ENROLLMENT_PRICE_USD, PaymentError, type PaymentLinks, type PhoneBuyOptions, type PhoneBuyResponse, PhoneClient, type PhoneClientOptions, type PhoneListResponse, type PhoneLookupResponse, type PhoneNumberRecord, type PhoneReleaseResponse, type PhoneRenewResponse, type PollOptions, PortraitClient, type PortraitClientOptions, type PortraitEnrollOptions, type PortraitEnrollResponse, type PriceBar, type PriceCategory, PriceClient, type PriceClientOptions, type PriceHistoryResponse, type PriceOptions, type PricePoint, RPC_PRICE_USD, type ResponseFormat, type RoutingDecision, type RoutingProfile, type RoutingTier, type RpcBatchRequest, RpcClient, type RpcClientOptions, type RpcError, type RpcNetwork, type RpcResponse, type RssSearchSource, SOLANA_NETWORK, SOLANA_WALLET_FILE as SOLANA_WALLET_FILE_PATH, SUPPORTED_NETWORKS, SearchClient, type SearchClientOptions, type SearchOptions, type SearchParameters, type SearchResult, type SearchSource, type SearchUsage, type SmartChatOptions, type SmartChatResponse, SolanaLLMClient, type SolanaLLMClientOptions, type SolanaWalletInfo, type SoundEffectOptions, type SpeechAudio, SpeechClient, type SpeechClientOptions, type SpeechGenerateOptions, type SpeechModel, type SpeechResponse, type SpeechVoice, type Spending, type SpendingReport, type StockMarket, SurfClient, type SurfClientOptions, type SymbolListResponse, type Tool, type ToolCall, type ToolChoice, USDC_BASE, USDC_BASE_CONTRACT, USDC_SOLANA, VideoClient, type VideoClientOptions, type VideoClip, type VideoGenerateOptions, type VideoModel, type VideoResponse, VoiceClient, type VoiceClientOptions, type VoiceInfo, type VoicePreset, WALLET_DIR_PATH, WALLET_FILE_PATH, type WalletInfo, type WebSearchSource, type XSearchSource, clearCache, createPaymentPayload, createSolanaPaymentPayload, createSolanaWallet, createWallet, LLMClient as default, extractPaymentDetails, formatFundingMessageCompact, formatNeedsFundingMessage, formatSolanaWalletMigrationNotice, formatWalletCreatedMessage, formatWalletMigrationNotice, getCached, getCachedByRequest, getCostLogSummary, getCostSummary, getEip681Uri, getOrCreateSolanaWallet, getOrCreateWallet, getPaymentLinks, getWalletAddress, importSolanaWallet, importWallet, listDiscoveredSolanaWallets, listDiscoveredWallets, loadSolanaWallet, loadWallet, logCost, parsePaymentRequired, saveSolanaWallet, saveToCache, saveWallet, scanSolanaWallets, scanWallets, setCache, setupAgentSolanaWallet, setupAgentWallet, solanaClient, solanaKeyToBytes, solanaPublicKey, status, validateMaxTokens, validateModel, validateTemperature, validateTopP };
|
package/dist/index.js
CHANGED
|
@@ -31,8 +31,47 @@ var BASE_CHAIN_ID = 8453;
|
|
|
31
31
|
var USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
32
32
|
var SOLANA_NETWORK = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
|
|
33
33
|
var USDC_SOLANA = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
34
|
+
var SOLANA_USDC_DECIMALS = 6;
|
|
34
35
|
var DEFAULT_COMPUTE_UNIT_PRICE_MICROLAMPORTS = 1;
|
|
35
36
|
var DEFAULT_COMPUTE_UNIT_LIMIT = 8e3;
|
|
37
|
+
var BLOCKHASH_TTL_MS = 1e4;
|
|
38
|
+
var MAX_FEE_NONCE_STEPS = 64;
|
|
39
|
+
var MAX_TRACKED_BLOCKHASHES = 8;
|
|
40
|
+
var MAX_TRACKED_ENDPOINTS = 8;
|
|
41
|
+
var blockhashCache = /* @__PURE__ */ new Map();
|
|
42
|
+
function trimOldest(map, limit) {
|
|
43
|
+
while (map.size > limit) {
|
|
44
|
+
const oldest = map.keys().next().value;
|
|
45
|
+
map.delete(oldest);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
var issuedByBlockhash = /* @__PURE__ */ new Map();
|
|
49
|
+
function issuedFor(blockhash) {
|
|
50
|
+
let issued = issuedByBlockhash.get(blockhash);
|
|
51
|
+
if (!issued) {
|
|
52
|
+
issued = /* @__PURE__ */ new Set();
|
|
53
|
+
issuedByBlockhash.set(blockhash, issued);
|
|
54
|
+
trimOldest(issuedByBlockhash, MAX_TRACKED_BLOCKHASHES);
|
|
55
|
+
}
|
|
56
|
+
return issued;
|
|
57
|
+
}
|
|
58
|
+
async function getBlockhashEntry(connection, rpcUrl, forceRefresh) {
|
|
59
|
+
const now = Date.now();
|
|
60
|
+
const cached = blockhashCache.get(rpcUrl);
|
|
61
|
+
if (!forceRefresh && cached && now - cached.fetchedAt < BLOCKHASH_TTL_MS) {
|
|
62
|
+
return cached;
|
|
63
|
+
}
|
|
64
|
+
const { blockhash } = await connection.getLatestBlockhash();
|
|
65
|
+
const current = blockhashCache.get(rpcUrl);
|
|
66
|
+
if (current?.blockhash === blockhash) {
|
|
67
|
+
current.fetchedAt = now;
|
|
68
|
+
return current;
|
|
69
|
+
}
|
|
70
|
+
const entry = { blockhash, fetchedAt: now };
|
|
71
|
+
blockhashCache.set(rpcUrl, entry);
|
|
72
|
+
trimOldest(blockhashCache, MAX_TRACKED_ENDPOINTS);
|
|
73
|
+
return entry;
|
|
74
|
+
}
|
|
36
75
|
var USDC_DOMAIN = {
|
|
37
76
|
name: "USD Coin",
|
|
38
77
|
version: "2",
|
|
@@ -117,7 +156,7 @@ async function createPaymentPayload(privateKey, fromAddress, recipient, amount,
|
|
|
117
156
|
}
|
|
118
157
|
async function createSolanaPaymentPayload(secretKey, fromAddress, recipient, amount, feePayer, options = {}) {
|
|
119
158
|
const { Connection, PublicKey, TransactionMessage, VersionedTransaction, ComputeBudgetProgram } = await import("@solana/web3.js");
|
|
120
|
-
const { getAssociatedTokenAddress, createTransferCheckedInstruction
|
|
159
|
+
const { getAssociatedTokenAddress, createTransferCheckedInstruction } = await import("@solana/spl-token");
|
|
121
160
|
const { Keypair } = await import("@solana/web3.js");
|
|
122
161
|
const rpcUrl = options.rpcUrl || "https://sol.blockrun.ai/api/v1/solana/rpc";
|
|
123
162
|
const connection = options.rpcHeaders ? new Connection(rpcUrl, { httpHeaders: options.rpcHeaders }) : new Connection(rpcUrl);
|
|
@@ -126,32 +165,53 @@ async function createSolanaPaymentPayload(secretKey, fromAddress, recipient, amo
|
|
|
126
165
|
const ownerPubkey = keypair.publicKey;
|
|
127
166
|
const tokenMint = new PublicKey(USDC_SOLANA);
|
|
128
167
|
const payToPubkey = new PublicKey(recipient);
|
|
129
|
-
const mintInfo = await getMint(connection, tokenMint);
|
|
130
168
|
const sourceATA = await getAssociatedTokenAddress(tokenMint, ownerPubkey, false);
|
|
131
169
|
const destinationATA = await getAssociatedTokenAddress(tokenMint, payToPubkey, false);
|
|
132
|
-
const { blockhash } = await connection.getLatestBlockhash();
|
|
133
|
-
const setComputeUnitPriceIx = ComputeBudgetProgram.setComputeUnitPrice({
|
|
134
|
-
microLamports: DEFAULT_COMPUTE_UNIT_PRICE_MICROLAMPORTS
|
|
135
|
-
});
|
|
136
|
-
const setComputeUnitLimitIx = ComputeBudgetProgram.setComputeUnitLimit({
|
|
137
|
-
units: DEFAULT_COMPUTE_UNIT_LIMIT
|
|
138
|
-
});
|
|
139
170
|
const transferIx = createTransferCheckedInstruction(
|
|
140
171
|
sourceATA,
|
|
141
172
|
tokenMint,
|
|
142
173
|
destinationATA,
|
|
143
174
|
ownerPubkey,
|
|
144
175
|
BigInt(amount),
|
|
145
|
-
|
|
176
|
+
SOLANA_USDC_DECIMALS
|
|
146
177
|
);
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
178
|
+
const buildSignedTx = (blockhash, unitPriceMicroLamports) => {
|
|
179
|
+
const messageV0 = new TransactionMessage({
|
|
180
|
+
payerKey: feePayerPubkey,
|
|
181
|
+
recentBlockhash: blockhash,
|
|
182
|
+
instructions: [
|
|
183
|
+
ComputeBudgetProgram.setComputeUnitLimit({ units: DEFAULT_COMPUTE_UNIT_LIMIT }),
|
|
184
|
+
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: unitPriceMicroLamports }),
|
|
185
|
+
transferIx
|
|
186
|
+
]
|
|
187
|
+
}).compileToV0Message();
|
|
188
|
+
const transaction = new VersionedTransaction(messageV0);
|
|
189
|
+
transaction.sign([keypair]);
|
|
190
|
+
return Buffer.from(transaction.serialize()).toString("base64");
|
|
191
|
+
};
|
|
192
|
+
const findDistinctTx = (candidate) => {
|
|
193
|
+
const issued = issuedFor(candidate.blockhash);
|
|
194
|
+
for (let step = 0; step <= MAX_FEE_NONCE_STEPS; step++) {
|
|
195
|
+
const tx = buildSignedTx(
|
|
196
|
+
candidate.blockhash,
|
|
197
|
+
DEFAULT_COMPUTE_UNIT_PRICE_MICROLAMPORTS + step
|
|
198
|
+
);
|
|
199
|
+
if (!issued.has(tx)) return tx;
|
|
200
|
+
}
|
|
201
|
+
return null;
|
|
202
|
+
};
|
|
203
|
+
let entry = await getBlockhashEntry(connection, rpcUrl, false);
|
|
204
|
+
let serializedTx = findDistinctTx(entry);
|
|
205
|
+
if (serializedTx === null) {
|
|
206
|
+
entry = await getBlockhashEntry(connection, rpcUrl, true);
|
|
207
|
+
serializedTx = findDistinctTx(entry);
|
|
208
|
+
}
|
|
209
|
+
if (serializedTx === null) {
|
|
210
|
+
throw new Error(
|
|
211
|
+
`Solana payment: ${MAX_FEE_NONCE_STEPS + 1} identical payments already issued against blockhash ${entry.blockhash}. Retry once it rotates.`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
issuedFor(entry.blockhash).add(serializedTx);
|
|
155
215
|
const paymentData = {
|
|
156
216
|
x402Version: 2,
|
|
157
217
|
resource: {
|
|
@@ -242,7 +302,6 @@ function validateModel(model) {
|
|
|
242
302
|
throw new Error("Model must be a non-empty string");
|
|
243
303
|
}
|
|
244
304
|
}
|
|
245
|
-
var MAX_TOKENS_SANITY_LIMIT = 1e6;
|
|
246
305
|
function validateMaxTokens(maxTokens) {
|
|
247
306
|
if (maxTokens === void 0 || maxTokens === null) {
|
|
248
307
|
return;
|
|
@@ -253,14 +312,6 @@ function validateMaxTokens(maxTokens) {
|
|
|
253
312
|
if (maxTokens < 1) {
|
|
254
313
|
throw new Error("maxTokens must be positive (minimum: 1)");
|
|
255
314
|
}
|
|
256
|
-
if (maxTokens > MAX_TOKENS_SANITY_LIMIT) {
|
|
257
|
-
throw Object.assign(
|
|
258
|
-
new Error(
|
|
259
|
-
`maxTokens implausibly large (client-side sanity limit: ${MAX_TOKENS_SANITY_LIMIT}). This is not a model limit \u2014 the gateway enforces the real per-model ceiling and reports it.`
|
|
260
|
-
),
|
|
261
|
-
{ code: "MAX_TOKENS_SANITY_LIMIT", limit: MAX_TOKENS_SANITY_LIMIT }
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
315
|
}
|
|
265
316
|
function validateTemperature(temperature) {
|
|
266
317
|
if (temperature === void 0 || temperature === null) {
|
|
@@ -400,7 +451,7 @@ function getCostSummary() {
|
|
|
400
451
|
}
|
|
401
452
|
|
|
402
453
|
// src/version.ts
|
|
403
|
-
var SDK_VERSION = "3.8.
|
|
454
|
+
var SDK_VERSION = "3.8.4";
|
|
404
455
|
var USER_AGENT = `blockrun-ts/${SDK_VERSION}`;
|
|
405
456
|
|
|
406
457
|
// src/client.ts
|
|
@@ -6024,7 +6075,6 @@ export {
|
|
|
6024
6075
|
ImageClient,
|
|
6025
6076
|
KNOWN_PROVIDERS,
|
|
6026
6077
|
LLMClient,
|
|
6027
|
-
MAX_TOKENS_SANITY_LIMIT,
|
|
6028
6078
|
MusicClient,
|
|
6029
6079
|
NETWORK_ALIASES,
|
|
6030
6080
|
OpenAI,
|