@blockrun/llm 3.23.2 → 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
 
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.2";
4859
+ var SDK_VERSION = "3.24.0";
4837
4860
  var USER_AGENT = `blockrun-ts/${SDK_VERSION}`;
4838
4861
 
4839
4862
  // src/client.ts
@@ -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
  /**
@@ -12567,6 +12591,7 @@ var SolanaLLMClient = class {
12567
12591
  ...(model.billingMode ?? raw.billing_mode) === "flat" && flatPrice > 0 ? { flatPrice } : {}
12568
12592
  });
12569
12593
  }
12594
+ registerSearchModels(pricing, models);
12570
12595
  return pricing;
12571
12596
  })();
12572
12597
  try {
package/dist/index.d.cts CHANGED
@@ -2953,8 +2953,9 @@ interface SolanaBatchOptions {
2953
2953
  * re-read, in addition to the one-line log the SDK writes to stderr for each.
2954
2954
  * A callback that throws, or returns a promise that rejects, is logged and
2955
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.
2956
2957
  */
2957
- onEvent?: (event: SolanaBatchEvent) => void | PromiseLike<void>;
2958
+ onEvent?: (event: SolanaBatchEvent) => unknown;
2958
2959
  }
2959
2960
  /** Default {@link SolanaBatchOptions.rateLimit}. */
2960
2961
  declare const DEFAULT_BATCH_RATE_LIMIT: {
package/dist/index.d.ts CHANGED
@@ -2953,8 +2953,9 @@ interface SolanaBatchOptions {
2953
2953
  * re-read, in addition to the one-line log the SDK writes to stderr for each.
2954
2954
  * A callback that throws, or returns a promise that rejects, is logged and
2955
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.
2956
2957
  */
2957
- onEvent?: (event: SolanaBatchEvent) => void | PromiseLike<void>;
2958
+ onEvent?: (event: SolanaBatchEvent) => unknown;
2958
2959
  }
2959
2960
  /** Default {@link SolanaBatchOptions.rateLimit}. */
2960
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.2";
4728
+ var SDK_VERSION = "3.24.0";
4706
4729
  var USER_AGENT = `blockrun-ts/${SDK_VERSION}`;
4707
4730
 
4708
4731
  // src/client.ts
@@ -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
  /**
@@ -12436,6 +12460,7 @@ var SolanaLLMClient = class {
12436
12460
  ...(model.billingMode ?? raw.billing_mode) === "flat" && flatPrice > 0 ? { flatPrice } : {}
12437
12461
  });
12438
12462
  }
12463
+ registerSearchModels(pricing, models);
12439
12464
  return pricing;
12440
12465
  })();
12441
12466
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/llm",
3
- "version": "3.23.2",
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",