@blockrun/llm 3.23.1 → 3.24.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
@@ -444,10 +444,13 @@ picked:
444
444
  **Search and routing.** xAI search (`x_search` / `web_search` tools, or
445
445
  `search` / `searchParameters` not set to `mode: 'off'`) runs on `xai/*` models
446
446
  only. When a routed call asks for search, the candidate chain is narrowed to
447
- its `xai/*` models in the router's order; when the route has none, `route()` /
448
- `smartChat()` / `smartChatCompletion()` throw before anything is sent or paid.
449
- For search, calling an `xai/*` model directly (e.g. `xai/grok-4.3`) is the
450
- dependable path.
447
+ its `xai/*` models in the router's order. The router seldom ranks xAI, so when
448
+ the route has none, the call falls back to the catalog's search-capable xAI
449
+ models (those `/v1/models` tags `search`): cheapest first on `eco` / `auto`,
450
+ most capable first on `premium`. `routing.reasoning` says when that happened.
451
+ Only a catalog with no `xai/*` chat model at all makes `route()` /
452
+ `smartChat()` / `smartChatCompletion()` throw, before anything is sent or paid.
453
+ Pin an `xai/*` model yourself when you want a specific one.
451
454
 
452
455
  ### TypeScript Types
453
456
 
@@ -1600,9 +1603,9 @@ await status();
1600
1603
 
1601
1604
  `getOrCreateWallet()` resolves the Base wallet in a fixed order:
1602
1605
  `BLOCKRUN_WALLET_KEY` or `BASE_CHAIN_WALLET_KEY`, then `~/.blockrun/.session`,
1603
- then the legacy `~/.blockrun/wallet.key`. `LLMClient` itself reads only
1604
- `BASE_CHAIN_WALLET_KEY` (or an explicit `privateKey`). For a single run of the
1605
- helpers without changing anything, use `export BLOCKRUN_WALLET_KEY=<private-key>`.
1606
+ then the legacy `~/.blockrun/wallet.key`. `LLMClient` reads an explicit
1607
+ `privateKey`, then `BASE_CHAIN_WALLET_KEY`, then `BLOCKRUN_WALLET_KEY`. For a
1608
+ single run without changing anything, use `export BLOCKRUN_WALLET_KEY=<private-key>`.
1606
1609
 
1607
1610
  ### Wallet discovery and migration
1608
1611
 
@@ -1686,7 +1689,7 @@ const configured = new LLMClient({
1686
1689
  | `BLOCKRUN_API_KEY` | Account key (`brk_...`): account billing, no wallet needed. Create one at [user.blockrun.ai/dashboard/keys](https://user.blockrun.ai/dashboard/keys) |
1687
1690
  | `BLOCKRUN_API_BASE_URL` | Account API endpoint (default `https://api.blockrun.ai`) |
1688
1691
  | `BASE_CHAIN_WALLET_KEY` | EVM wallet private key for `LLMClient` (Base / Arc) and every EVM client |
1689
- | `BLOCKRUN_WALLET_KEY` | EVM wallet key honoured by the service clients, `BlockrunClient`, `setupAgentWallet()` and `AnthropicClient` (checked before `BASE_CHAIN_WALLET_KEY`); **not** read by `LLMClient` |
1692
+ | `BLOCKRUN_WALLET_KEY` | EVM wallet key read by every EVM client. The service clients, `BlockrunClient`, `setupAgentWallet()` and `AnthropicClient` check it before `BASE_CHAIN_WALLET_KEY`; `LLMClient` checks it after, so a process holding both keeps paying from the same wallet |
1690
1693
  | `SOLANA_WALLET_KEY` | Solana secret key, bs58-encoded, for `SolanaLLMClient` |
1691
1694
  | `SOLANA_RPC_URL` | Solana RPC for payments and balance (also `rpcUrl`) |
1692
1695
  | `SOLANA_RPC_API_KEY` / `SOLANA_RPC_HEADERS` | Header auth for that RPC (also `rpcHeaders`) |
package/dist/index.cjs CHANGED
@@ -4359,6 +4359,24 @@ function requestsSearch(options) {
4359
4359
  if (options.searchParameters !== void 0) return options.searchParameters.mode !== "off";
4360
4360
  return options.search === true;
4361
4361
  }
4362
+ var searchModelsByCatalog = /* @__PURE__ */ new WeakMap();
4363
+ function registerSearchModels(pricing, models) {
4364
+ const xaiChat = models.filter(
4365
+ (m) => m.id.startsWith("xai/") && m.available !== false && Array.isArray(m.categories) && m.categories.includes("chat") && pricing.has(m.id)
4366
+ );
4367
+ const tagged = xaiChat.filter((m) => m.categories.includes("search"));
4368
+ searchModelsByCatalog.set(pricing, (tagged.length > 0 ? tagged : xaiChat).map((m) => m.id));
4369
+ }
4370
+ function searchFallbackChain(modelPricing, routingProfile) {
4371
+ const pool = searchModelsByCatalog.get(modelPricing) ?? [...modelPricing.keys()].filter((id) => id.startsWith("xai/"));
4372
+ const price = (id) => {
4373
+ const p = modelPricing.get(id);
4374
+ return p ? p.inputPrice + p.outputPrice : Number.POSITIVE_INFINITY;
4375
+ };
4376
+ const sorted = [...pool].sort((a, b) => price(a) - price(b) || b.localeCompare(a, void 0, { numeric: true }));
4377
+ if (routingProfile !== "premium") return sorted;
4378
+ return [...pool].sort((a, b) => price(b) - price(a) || b.localeCompare(a, void 0, { numeric: true }));
4379
+ }
4362
4380
  function routingProfileForModel(model) {
4363
4381
  return AUTO_ROUTING_PROFILES[model.toLowerCase()];
4364
4382
  }
@@ -4406,11 +4424,16 @@ function routeWithCatalog(prompt, systemPrompt, maxOutputTokens, modelPricing, o
4406
4424
  const resolved = !id.startsWith("free/") ? id : modelPricing.has(`nvidia/${id.slice(5)}`) ? `nvidia/${id.slice(5)}` : null;
4407
4425
  if (resolved && !callable.includes(resolved)) callable.push(resolved);
4408
4426
  }
4427
+ let searchFallback = false;
4409
4428
  if (requestsSearch(options)) {
4410
- const searchable = callable.filter((id) => id.startsWith("xai/"));
4429
+ let searchable = callable.filter((id) => id.startsWith("xai/"));
4430
+ if (searchable.length === 0) {
4431
+ searchable = searchFallbackChain(modelPricing, options.routingProfile);
4432
+ searchFallback = true;
4433
+ }
4411
4434
  if (searchable.length === 0) {
4412
4435
  throw new Error(
4413
- `Search (x_search / web_search tools or searchParameters) runs on xai/* models only, and the ${options.routingProfile ?? "auto"} route has no xai/* candidate for this request. Call an xai/* model directly (e.g. xai/grok-4.3) or drop the search options.`
4436
+ `Search (x_search / web_search tools or searchParameters) runs on xai/* models only, and the catalog lists no available xai/* chat model. Call an xai/* model directly or drop the search options.`
4414
4437
  );
4415
4438
  }
4416
4439
  callable.splice(0, callable.length, ...searchable);
@@ -4447,7 +4470,7 @@ function routeWithCatalog(prompt, systemPrompt, maxOutputTokens, modelPricing, o
4447
4470
  costEstimate,
4448
4471
  savings,
4449
4472
  model,
4450
- reasoning: model === decision.model ? decision.reasoning : `${decision.reasoning} | catalog fallback: ${model}`,
4473
+ reasoning: searchFallback ? `${decision.reasoning} | search runs on xai/* only; the route had none, so the ${options.routingProfile === "premium" ? "most capable" : "cheapest"} search-capable xAI model: ${model}` : model === decision.model ? decision.reasoning : `${decision.reasoning} | catalog fallback: ${model}`,
4451
4474
  candidates: availableCandidates,
4452
4475
  candidateScores: decision.candidateScores?.filter(
4453
4476
  (score) => availableCandidates.includes(score.model)
@@ -4833,7 +4856,7 @@ function getCostSummary() {
4833
4856
  }
4834
4857
 
4835
4858
  // src/version.ts
4836
- var SDK_VERSION = "3.23.1";
4859
+ var SDK_VERSION = "3.24.0";
4837
4860
  var USER_AGENT = `blockrun-ts/${SDK_VERSION}`;
4838
4861
 
4839
4862
  // src/client.ts
@@ -4908,16 +4931,16 @@ var LLMClient = class _LLMClient {
4908
4931
  /**
4909
4932
  * Initialize the BlockRun LLM client.
4910
4933
  *
4911
- * @param options - Client configuration options (optional if BASE_CHAIN_WALLET_KEY env var is set)
4934
+ * @param options - Client configuration options (optional if BLOCKRUN_API_KEY, BASE_CHAIN_WALLET_KEY or BLOCKRUN_WALLET_KEY is set)
4912
4935
  */
4913
4936
  constructor(options = {}) {
4914
4937
  this.apiAuth = resolveApiKeyAuth(options);
4915
4938
  if (!this.apiAuth) {
4916
- const envKey = typeof process !== "undefined" && process.env ? process.env.BASE_CHAIN_WALLET_KEY : void 0;
4939
+ const envKey = typeof process !== "undefined" && process.env ? process.env.BASE_CHAIN_WALLET_KEY || process.env.BLOCKRUN_WALLET_KEY : void 0;
4917
4940
  const privateKey = options.privateKey || envKey;
4918
4941
  if (!privateKey) {
4919
4942
  throw new Error(
4920
- "Private key required. Pass privateKey in options or set BASE_CHAIN_WALLET_KEY environment variable."
4943
+ "Private key required. Pass privateKey in options or set BASE_CHAIN_WALLET_KEY (or BLOCKRUN_WALLET_KEY) environment variable."
4921
4944
  );
4922
4945
  }
4923
4946
  validatePrivateKey(privateKey);
@@ -5104,6 +5127,7 @@ var LLMClient = class _LLMClient {
5104
5127
  pricing.set(model.id, { inputPrice, outputPrice });
5105
5128
  }
5106
5129
  }
5130
+ registerSearchModels(pricing, models);
5107
5131
  return pricing;
5108
5132
  }
5109
5133
  /**
@@ -5606,7 +5630,7 @@ var LLMClient = class _LLMClient {
5606
5630
  }
5607
5631
  /**
5608
5632
  * Make a request with automatic x402 payment handling, returning raw JSON.
5609
- * Used for non-ChatResponse endpoints (X/Twitter, search, image edit, etc.).
5633
+ * Used for non-ChatResponse endpoints (search, image edit, etc.).
5610
5634
  */
5611
5635
  async requestWithPaymentRaw(endpoint, body) {
5612
5636
  const url = `${this.apiUrl}${endpoint}`;
@@ -6014,7 +6038,8 @@ var LLMClient = class _LLMClient {
6014
6038
  return data;
6015
6039
  }
6016
6040
  /**
6017
- * Standalone search (web, X/Twitter, news).
6041
+ * Standalone search over web and news. For X/Twitter, use chat on an
6042
+ * `xai/*` model with an `x_search` tool.
6018
6043
  *
6019
6044
  * @param query - Search query
6020
6045
  * @param options - Optional search parameters
@@ -8508,6 +8533,7 @@ var import_accounts12 = require("viem/accounts");
8508
8533
  var DEFAULT_API_URL11 = "https://blockrun.ai/api";
8509
8534
  var DEFAULT_TIMEOUT11 = 6e4;
8510
8535
  var SURF_PREFIX = "/v1/surf";
8536
+ var warnedRetired = false;
8511
8537
  var SurfClient = class {
8512
8538
  _account;
8513
8539
  get account() {
@@ -8525,6 +8551,12 @@ var SurfClient = class {
8525
8551
  apiUrl;
8526
8552
  timeout;
8527
8553
  constructor(options = {}) {
8554
+ if (!warnedRetired) {
8555
+ warnedRetired = true;
8556
+ console.warn(
8557
+ "[blockrun] SurfClient is deprecated: the gateway retired /v1/surf/* (410 endpoint_retired). Use PriceClient, LLMClient.defi*, LLMClient.pm() or LLMClient.dex* instead."
8558
+ );
8559
+ }
8528
8560
  this.apiAuth = resolveApiKeyAuth(options);
8529
8561
  if (!this.apiAuth) {
8530
8562
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
@@ -12559,6 +12591,7 @@ var SolanaLLMClient = class {
12559
12591
  ...(model.billingMode ?? raw.billing_mode) === "flat" && flatPrice > 0 ? { flatPrice } : {}
12560
12592
  });
12561
12593
  }
12594
+ registerSearchModels(pricing, models);
12562
12595
  return pricing;
12563
12596
  })();
12564
12597
  try {
package/dist/index.d.cts CHANGED
@@ -499,7 +499,7 @@ interface PaymentRequired {
499
499
  extensions?: Record<string, unknown>;
500
500
  }
501
501
  interface LLMClientOptions extends ApiKeyOptions {
502
- /** EVM wallet private key (hex string starting with 0x). Optional if BASE_CHAIN_WALLET_KEY env var is set. */
502
+ /** EVM wallet private key (hex string starting with 0x). Optional if BASE_CHAIN_WALLET_KEY (or BLOCKRUN_WALLET_KEY) is set. */
503
503
  privateKey?: `0x${string}` | string;
504
504
  /** API endpoint URL (default: https://blockrun.ai/api) */
505
505
  apiUrl?: string;
@@ -975,7 +975,10 @@ interface VideoGenerateOptions {
975
975
  returnLastFrame?: boolean;
976
976
  }
977
977
  interface SearchOptions {
978
- /** Source types to search (e.g. ["web", "x", "news"]) */
978
+ /**
979
+ * Source types to search: `"web"` and/or `"news"` (default `["web"]`). `"x"` is
980
+ * rejected by the gateway; X search runs in chat on `xai/*` models (`x_search`).
981
+ */
979
982
  sources?: string[];
980
983
  /** Maximum number of results (default: 10) */
981
984
  maxResults?: number;
@@ -1119,6 +1122,7 @@ interface PriceClientOptions extends ApiKeyOptions {
1119
1122
  /** If false, construction succeeds without a wallet (free endpoints only). */
1120
1123
  requireWallet?: boolean;
1121
1124
  }
1125
+ /** @deprecated Surf is retired; see {@link SurfClient}. */
1122
1126
  interface SurfClientOptions extends ApiKeyOptions {
1123
1127
  privateKey?: `0x${string}` | string;
1124
1128
  apiUrl?: string;
@@ -1351,7 +1355,7 @@ declare class LLMClient {
1351
1355
  /**
1352
1356
  * Initialize the BlockRun LLM client.
1353
1357
  *
1354
- * @param options - Client configuration options (optional if BASE_CHAIN_WALLET_KEY env var is set)
1358
+ * @param options - Client configuration options (optional if BLOCKRUN_API_KEY, BASE_CHAIN_WALLET_KEY or BLOCKRUN_WALLET_KEY is set)
1355
1359
  */
1356
1360
  constructor(options?: LLMClientOptions);
1357
1361
  /**
@@ -1510,7 +1514,7 @@ declare class LLMClient {
1510
1514
  chatCompletionStream(model: string, messages: ChatMessage[], options?: ChatCompletionOptions): Promise<Response>;
1511
1515
  /**
1512
1516
  * Make a request with automatic x402 payment handling, returning raw JSON.
1513
- * Used for non-ChatResponse endpoints (X/Twitter, search, image edit, etc.).
1517
+ * Used for non-ChatResponse endpoints (search, image edit, etc.).
1514
1518
  */
1515
1519
  private requestWithPaymentRaw;
1516
1520
  /**
@@ -1574,7 +1578,8 @@ declare class LLMClient {
1574
1578
  */
1575
1579
  imageEdit(prompt: string, image: string | string[], options?: ImageEditOptions): Promise<ImageResponse>;
1576
1580
  /**
1577
- * Standalone search (web, X/Twitter, news).
1581
+ * Standalone search over web and news. For X/Twitter, use chat on an
1582
+ * `xai/*` model with an `x_search` tool.
1578
1583
  *
1579
1584
  * @param query - Search query
1580
1585
  * @param options - Optional search parameters
@@ -2328,6 +2333,7 @@ declare class PriceClient {
2328
2333
 
2329
2334
  type QueryValue$1 = string | number | boolean | null | undefined;
2330
2335
  type QueryParams$1 = Record<string, QueryValue$1 | QueryValue$1[]>;
2336
+ /** @deprecated Surf is retired (`410 endpoint_retired`); see the module docs for replacements. */
2331
2337
  declare class SurfClient {
2332
2338
  private _account?;
2333
2339
  private get account();
@@ -2947,8 +2953,9 @@ interface SolanaBatchOptions {
2947
2953
  * re-read, in addition to the one-line log the SDK writes to stderr for each.
2948
2954
  * A callback that throws, or returns a promise that rejects, is logged and
2949
2955
  * ignored; it never affects the payment, and its promise is not awaited.
2956
+ * Any other return value is ignored, so `(e) => events.push(e)` is fine.
2950
2957
  */
2951
- onEvent?: (event: SolanaBatchEvent) => void | PromiseLike<void>;
2958
+ onEvent?: (event: SolanaBatchEvent) => unknown;
2952
2959
  }
2953
2960
  /** Default {@link SolanaBatchOptions.rateLimit}. */
2954
2961
  declare const DEFAULT_BATCH_RATE_LIMIT: {
package/dist/index.d.ts CHANGED
@@ -499,7 +499,7 @@ interface PaymentRequired {
499
499
  extensions?: Record<string, unknown>;
500
500
  }
501
501
  interface LLMClientOptions extends ApiKeyOptions {
502
- /** EVM wallet private key (hex string starting with 0x). Optional if BASE_CHAIN_WALLET_KEY env var is set. */
502
+ /** EVM wallet private key (hex string starting with 0x). Optional if BASE_CHAIN_WALLET_KEY (or BLOCKRUN_WALLET_KEY) is set. */
503
503
  privateKey?: `0x${string}` | string;
504
504
  /** API endpoint URL (default: https://blockrun.ai/api) */
505
505
  apiUrl?: string;
@@ -975,7 +975,10 @@ interface VideoGenerateOptions {
975
975
  returnLastFrame?: boolean;
976
976
  }
977
977
  interface SearchOptions {
978
- /** Source types to search (e.g. ["web", "x", "news"]) */
978
+ /**
979
+ * Source types to search: `"web"` and/or `"news"` (default `["web"]`). `"x"` is
980
+ * rejected by the gateway; X search runs in chat on `xai/*` models (`x_search`).
981
+ */
979
982
  sources?: string[];
980
983
  /** Maximum number of results (default: 10) */
981
984
  maxResults?: number;
@@ -1119,6 +1122,7 @@ interface PriceClientOptions extends ApiKeyOptions {
1119
1122
  /** If false, construction succeeds without a wallet (free endpoints only). */
1120
1123
  requireWallet?: boolean;
1121
1124
  }
1125
+ /** @deprecated Surf is retired; see {@link SurfClient}. */
1122
1126
  interface SurfClientOptions extends ApiKeyOptions {
1123
1127
  privateKey?: `0x${string}` | string;
1124
1128
  apiUrl?: string;
@@ -1351,7 +1355,7 @@ declare class LLMClient {
1351
1355
  /**
1352
1356
  * Initialize the BlockRun LLM client.
1353
1357
  *
1354
- * @param options - Client configuration options (optional if BASE_CHAIN_WALLET_KEY env var is set)
1358
+ * @param options - Client configuration options (optional if BLOCKRUN_API_KEY, BASE_CHAIN_WALLET_KEY or BLOCKRUN_WALLET_KEY is set)
1355
1359
  */
1356
1360
  constructor(options?: LLMClientOptions);
1357
1361
  /**
@@ -1510,7 +1514,7 @@ declare class LLMClient {
1510
1514
  chatCompletionStream(model: string, messages: ChatMessage[], options?: ChatCompletionOptions): Promise<Response>;
1511
1515
  /**
1512
1516
  * Make a request with automatic x402 payment handling, returning raw JSON.
1513
- * Used for non-ChatResponse endpoints (X/Twitter, search, image edit, etc.).
1517
+ * Used for non-ChatResponse endpoints (search, image edit, etc.).
1514
1518
  */
1515
1519
  private requestWithPaymentRaw;
1516
1520
  /**
@@ -1574,7 +1578,8 @@ declare class LLMClient {
1574
1578
  */
1575
1579
  imageEdit(prompt: string, image: string | string[], options?: ImageEditOptions): Promise<ImageResponse>;
1576
1580
  /**
1577
- * Standalone search (web, X/Twitter, news).
1581
+ * Standalone search over web and news. For X/Twitter, use chat on an
1582
+ * `xai/*` model with an `x_search` tool.
1578
1583
  *
1579
1584
  * @param query - Search query
1580
1585
  * @param options - Optional search parameters
@@ -2328,6 +2333,7 @@ declare class PriceClient {
2328
2333
 
2329
2334
  type QueryValue$1 = string | number | boolean | null | undefined;
2330
2335
  type QueryParams$1 = Record<string, QueryValue$1 | QueryValue$1[]>;
2336
+ /** @deprecated Surf is retired (`410 endpoint_retired`); see the module docs for replacements. */
2331
2337
  declare class SurfClient {
2332
2338
  private _account?;
2333
2339
  private get account();
@@ -2947,8 +2953,9 @@ interface SolanaBatchOptions {
2947
2953
  * re-read, in addition to the one-line log the SDK writes to stderr for each.
2948
2954
  * A callback that throws, or returns a promise that rejects, is logged and
2949
2955
  * ignored; it never affects the payment, and its promise is not awaited.
2956
+ * Any other return value is ignored, so `(e) => events.push(e)` is fine.
2950
2957
  */
2951
- onEvent?: (event: SolanaBatchEvent) => void | PromiseLike<void>;
2958
+ onEvent?: (event: SolanaBatchEvent) => unknown;
2952
2959
  }
2953
2960
  /** Default {@link SolanaBatchOptions.rateLimit}. */
2954
2961
  declare const DEFAULT_BATCH_RATE_LIMIT: {
package/dist/index.js CHANGED
@@ -4228,6 +4228,24 @@ function requestsSearch(options) {
4228
4228
  if (options.searchParameters !== void 0) return options.searchParameters.mode !== "off";
4229
4229
  return options.search === true;
4230
4230
  }
4231
+ var searchModelsByCatalog = /* @__PURE__ */ new WeakMap();
4232
+ function registerSearchModels(pricing, models) {
4233
+ const xaiChat = models.filter(
4234
+ (m) => m.id.startsWith("xai/") && m.available !== false && Array.isArray(m.categories) && m.categories.includes("chat") && pricing.has(m.id)
4235
+ );
4236
+ const tagged = xaiChat.filter((m) => m.categories.includes("search"));
4237
+ searchModelsByCatalog.set(pricing, (tagged.length > 0 ? tagged : xaiChat).map((m) => m.id));
4238
+ }
4239
+ function searchFallbackChain(modelPricing, routingProfile) {
4240
+ const pool = searchModelsByCatalog.get(modelPricing) ?? [...modelPricing.keys()].filter((id) => id.startsWith("xai/"));
4241
+ const price = (id) => {
4242
+ const p = modelPricing.get(id);
4243
+ return p ? p.inputPrice + p.outputPrice : Number.POSITIVE_INFINITY;
4244
+ };
4245
+ const sorted = [...pool].sort((a, b) => price(a) - price(b) || b.localeCompare(a, void 0, { numeric: true }));
4246
+ if (routingProfile !== "premium") return sorted;
4247
+ return [...pool].sort((a, b) => price(b) - price(a) || b.localeCompare(a, void 0, { numeric: true }));
4248
+ }
4231
4249
  function routingProfileForModel(model) {
4232
4250
  return AUTO_ROUTING_PROFILES[model.toLowerCase()];
4233
4251
  }
@@ -4275,11 +4293,16 @@ function routeWithCatalog(prompt, systemPrompt, maxOutputTokens, modelPricing, o
4275
4293
  const resolved = !id.startsWith("free/") ? id : modelPricing.has(`nvidia/${id.slice(5)}`) ? `nvidia/${id.slice(5)}` : null;
4276
4294
  if (resolved && !callable.includes(resolved)) callable.push(resolved);
4277
4295
  }
4296
+ let searchFallback = false;
4278
4297
  if (requestsSearch(options)) {
4279
- const searchable = callable.filter((id) => id.startsWith("xai/"));
4298
+ let searchable = callable.filter((id) => id.startsWith("xai/"));
4299
+ if (searchable.length === 0) {
4300
+ searchable = searchFallbackChain(modelPricing, options.routingProfile);
4301
+ searchFallback = true;
4302
+ }
4280
4303
  if (searchable.length === 0) {
4281
4304
  throw new Error(
4282
- `Search (x_search / web_search tools or searchParameters) runs on xai/* models only, and the ${options.routingProfile ?? "auto"} route has no xai/* candidate for this request. Call an xai/* model directly (e.g. xai/grok-4.3) or drop the search options.`
4305
+ `Search (x_search / web_search tools or searchParameters) runs on xai/* models only, and the catalog lists no available xai/* chat model. Call an xai/* model directly or drop the search options.`
4283
4306
  );
4284
4307
  }
4285
4308
  callable.splice(0, callable.length, ...searchable);
@@ -4316,7 +4339,7 @@ function routeWithCatalog(prompt, systemPrompt, maxOutputTokens, modelPricing, o
4316
4339
  costEstimate,
4317
4340
  savings,
4318
4341
  model,
4319
- reasoning: model === decision.model ? decision.reasoning : `${decision.reasoning} | catalog fallback: ${model}`,
4342
+ reasoning: searchFallback ? `${decision.reasoning} | search runs on xai/* only; the route had none, so the ${options.routingProfile === "premium" ? "most capable" : "cheapest"} search-capable xAI model: ${model}` : model === decision.model ? decision.reasoning : `${decision.reasoning} | catalog fallback: ${model}`,
4320
4343
  candidates: availableCandidates,
4321
4344
  candidateScores: decision.candidateScores?.filter(
4322
4345
  (score) => availableCandidates.includes(score.model)
@@ -4702,7 +4725,7 @@ function getCostSummary() {
4702
4725
  }
4703
4726
 
4704
4727
  // src/version.ts
4705
- var SDK_VERSION = "3.23.1";
4728
+ var SDK_VERSION = "3.24.0";
4706
4729
  var USER_AGENT = `blockrun-ts/${SDK_VERSION}`;
4707
4730
 
4708
4731
  // src/client.ts
@@ -4777,16 +4800,16 @@ var LLMClient = class _LLMClient {
4777
4800
  /**
4778
4801
  * Initialize the BlockRun LLM client.
4779
4802
  *
4780
- * @param options - Client configuration options (optional if BASE_CHAIN_WALLET_KEY env var is set)
4803
+ * @param options - Client configuration options (optional if BLOCKRUN_API_KEY, BASE_CHAIN_WALLET_KEY or BLOCKRUN_WALLET_KEY is set)
4781
4804
  */
4782
4805
  constructor(options = {}) {
4783
4806
  this.apiAuth = resolveApiKeyAuth(options);
4784
4807
  if (!this.apiAuth) {
4785
- const envKey = typeof process !== "undefined" && process.env ? process.env.BASE_CHAIN_WALLET_KEY : void 0;
4808
+ const envKey = typeof process !== "undefined" && process.env ? process.env.BASE_CHAIN_WALLET_KEY || process.env.BLOCKRUN_WALLET_KEY : void 0;
4786
4809
  const privateKey = options.privateKey || envKey;
4787
4810
  if (!privateKey) {
4788
4811
  throw new Error(
4789
- "Private key required. Pass privateKey in options or set BASE_CHAIN_WALLET_KEY environment variable."
4812
+ "Private key required. Pass privateKey in options or set BASE_CHAIN_WALLET_KEY (or BLOCKRUN_WALLET_KEY) environment variable."
4790
4813
  );
4791
4814
  }
4792
4815
  validatePrivateKey(privateKey);
@@ -4973,6 +4996,7 @@ var LLMClient = class _LLMClient {
4973
4996
  pricing.set(model.id, { inputPrice, outputPrice });
4974
4997
  }
4975
4998
  }
4999
+ registerSearchModels(pricing, models);
4976
5000
  return pricing;
4977
5001
  }
4978
5002
  /**
@@ -5475,7 +5499,7 @@ var LLMClient = class _LLMClient {
5475
5499
  }
5476
5500
  /**
5477
5501
  * Make a request with automatic x402 payment handling, returning raw JSON.
5478
- * Used for non-ChatResponse endpoints (X/Twitter, search, image edit, etc.).
5502
+ * Used for non-ChatResponse endpoints (search, image edit, etc.).
5479
5503
  */
5480
5504
  async requestWithPaymentRaw(endpoint, body) {
5481
5505
  const url = `${this.apiUrl}${endpoint}`;
@@ -5883,7 +5907,8 @@ var LLMClient = class _LLMClient {
5883
5907
  return data;
5884
5908
  }
5885
5909
  /**
5886
- * Standalone search (web, X/Twitter, news).
5910
+ * Standalone search over web and news. For X/Twitter, use chat on an
5911
+ * `xai/*` model with an `x_search` tool.
5887
5912
  *
5888
5913
  * @param query - Search query
5889
5914
  * @param options - Optional search parameters
@@ -8377,6 +8402,7 @@ import { privateKeyToAccount as privateKeyToAccount11 } from "viem/accounts";
8377
8402
  var DEFAULT_API_URL11 = "https://blockrun.ai/api";
8378
8403
  var DEFAULT_TIMEOUT11 = 6e4;
8379
8404
  var SURF_PREFIX = "/v1/surf";
8405
+ var warnedRetired = false;
8380
8406
  var SurfClient = class {
8381
8407
  _account;
8382
8408
  get account() {
@@ -8394,6 +8420,12 @@ var SurfClient = class {
8394
8420
  apiUrl;
8395
8421
  timeout;
8396
8422
  constructor(options = {}) {
8423
+ if (!warnedRetired) {
8424
+ warnedRetired = true;
8425
+ console.warn(
8426
+ "[blockrun] SurfClient is deprecated: the gateway retired /v1/surf/* (410 endpoint_retired). Use PriceClient, LLMClient.defi*, LLMClient.pm() or LLMClient.dex* instead."
8427
+ );
8428
+ }
8397
8429
  this.apiAuth = resolveApiKeyAuth(options);
8398
8430
  if (!this.apiAuth) {
8399
8431
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
@@ -12428,6 +12460,7 @@ var SolanaLLMClient = class {
12428
12460
  ...(model.billingMode ?? raw.billing_mode) === "flat" && flatPrice > 0 ? { flatPrice } : {}
12429
12461
  });
12430
12462
  }
12463
+ registerSearchModels(pricing, models);
12431
12464
  return pricing;
12432
12465
  })();
12433
12466
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/llm",
3
- "version": "3.23.1",
3
+ "version": "3.24.0",
4
4
  "type": "module",
5
5
  "description": "TypeScript SDK for BlockRun - every frontier model behind one OpenAI-compatible client. Pay with a BlockRun API key or per-call in USDC. Smart routing picks the cheapest capable model. No rate limits.",
6
6
  "main": "dist/index.cjs",