@blockrun/llm 3.16.0 → 3.17.1

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
@@ -4,7 +4,7 @@
4
4
 
5
5
  ### Cut your LLM bill by <!-- br:savings.autoVsBaselinePct -->84<!-- /br:savings.autoVsBaselinePct -->%. One line of TypeScript.
6
6
 
7
- The smart-routing SDK for <!-- br:models.chatVisible -->78<!-- /br:models.chatVisible --> models — every request goes to the cheapest model that can handle it,
7
+ The smart-routing SDK for <!-- br:models.chatVisible -->79<!-- /br:models.chatVisible --> models — every request goes to the cheapest model that can handle it,
8
8
  paid with an API key or per-request USDC on Solana or Base. No vendor lock-in.
9
9
 
10
10
  [![npm](https://img.shields.io/npm/v/@blockrun/llm.svg?style=flat-square)](https://www.npmjs.com/package/@blockrun/llm)
@@ -56,7 +56,7 @@ console.log(r.response); // the proof
56
56
  | | OpenAI SDK | OpenRouter | LiteLLM | **@blockrun/llm** |
57
57
  | ------------------ | -------------- | ----------------- | ---------------- | ----------------------------------------------------------------------- |
58
58
  | **Cost routing** | ✗ one vendor | Manual selection | Manual selection | **Automatic — <!-- br:savings.autoVsBaselinePct -->84<!-- /br:savings.autoVsBaselinePct -->% cheaper** |
59
- | **Models** | GPT only | 200+ | 100+ (BYO keys) | **<!-- br:models.chatVisible -->78<!-- /br:models.chatVisible -->, one credential** |
59
+ | **Models** | GPT only | 200+ | 100+ (BYO keys) | **<!-- br:models.chatVisible -->79<!-- /br:models.chatVisible -->, one credential** |
60
60
  | **Free tier** | ✗ | Rate-limited | ✗ | **<!-- br:models.free -->6<!-- /br:models.free --> models, no signup** |
61
61
  | **Auth** | API key | Account + API key | Your API keys | **API key *or* wallet signature** |
62
62
  | **Payment** | Card + invoice | Credit card | BYO keys | **Account credit or USDC per-request** |
@@ -1774,7 +1774,7 @@ The `AnthropicClient` wraps the official `@anthropic-ai/sdk` with a custom fetch
1774
1774
  ## Frequently Asked Questions
1775
1775
 
1776
1776
  ### What is @blockrun/llm?
1777
- @blockrun/llm is a TypeScript SDK that cuts LLM costs by up to <!-- br:savings.autoVsBaselinePct -->84<!-- /br:savings.autoVsBaselinePct -->% with built-in smart routing: every request is routed to the cheapest of <!-- br:models.chatVisible -->78<!-- /br:models.chatVisible --> models (OpenAI, Anthropic, Google, xAI, DeepSeek, Moonshot, and more) that can handle it, then paid per-request in USDC via the x402 protocol — with API key account billing or x402 wallet payments on Solana or Base.
1777
+ @blockrun/llm is a TypeScript SDK that cuts LLM costs by up to <!-- br:savings.autoVsBaselinePct -->84<!-- /br:savings.autoVsBaselinePct -->% with built-in smart routing: every request is routed to the cheapest of <!-- br:models.chatVisible -->79<!-- /br:models.chatVisible --> models (OpenAI, Anthropic, Google, xAI, DeepSeek, Moonshot, and more) that can handle it, then paid per-request in USDC via the x402 protocol — with API key account billing or x402 wallet payments on Solana or Base.
1778
1778
 
1779
1779
  ### How does payment work?
1780
1780
  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.
package/dist/index.cjs CHANGED
@@ -4660,7 +4660,7 @@ function getCostSummary() {
4660
4660
  }
4661
4661
 
4662
4662
  // src/version.ts
4663
- var SDK_VERSION = "3.16.0";
4663
+ var SDK_VERSION = "3.17.1";
4664
4664
  var USER_AGENT = `blockrun-ts/${SDK_VERSION}`;
4665
4665
 
4666
4666
  // src/client.ts
@@ -8715,6 +8715,15 @@ var BlockrunClient = class {
8715
8715
  * deadline exceeded). Settlement happens only when upstream returns 200 +
8716
8716
  * completed — upstream failure or caller giving up = no charge.
8717
8717
  *
8718
+ * **EVM only, and enforced upstream.** Replaying one signature works because
8719
+ * an EIP-3009 authorization stays valid for as long as it was signed for. A
8720
+ * Solana payment is a transaction pinned to a recent blockhash and expires in
8721
+ * ~150 blocks (~60s), so replaying one could not cover a long render. No
8722
+ * guard is needed here: `signFrom402` already refuses a `solana:` network
8723
+ * outright, so this loop is unreachable on Solana. Use `SolanaLLMClient`,
8724
+ * whose poll loop signs afresh each time; the gateway binds a job to the
8725
+ * payer address rather than to the signature, and still settles exactly once.
8726
+ *
8718
8727
  * If the gateway returns 200 directly on submit (no async surface), this
8719
8728
  * short-circuits and returns the body. Most long-running endpoints (image,
8720
8729
  * video, music, voice) return 202 with a poll_url.
@@ -9949,6 +9958,18 @@ var SolanaLLMClient = class {
9949
9958
  }
9950
9959
  }
9951
9960
  /** Edit an image using img2img (Solana payment). */
9961
+ /**
9962
+ * Generate an image on Solana.
9963
+ *
9964
+ * Slow models answer 202 `{ id, poll_url }`; `requestWithPaymentRaw` follows
9965
+ * that to completion, re-signing per poll, and settles once at the end. Fast
9966
+ * models answer 200 inline and settle there.
9967
+ */
9968
+ async image(prompt, options = {}) {
9969
+ const body = { prompt, ...options };
9970
+ const data = await this.requestWithPaymentRaw("/v1/images/generations", body);
9971
+ return data;
9972
+ }
9952
9973
  async imageEdit(prompt, image, options) {
9953
9974
  const body = {
9954
9975
  model: options?.model || "openai/gpt-image-2",
@@ -10196,6 +10217,86 @@ var SolanaLLMClient = class {
10196
10217
  * @returns the header value to replay with, and what it will settle for.
10197
10218
  * @throws PaymentError when the 402 carries no usable Solana requirements.
10198
10219
  */
10220
+ /**
10221
+ * Follow a 202 `{ id, poll_url }` to completion, on Solana.
10222
+ *
10223
+ * Two things differ from Base and both come from the same constraint — a
10224
+ * Solana payment is a transaction pinned to a recent blockhash, valid for
10225
+ * ~150 blocks (~60s), and a long render outlives it:
10226
+ *
10227
+ * 1. **The POST already settled.** Base settles on the completed poll; Solana
10228
+ * cannot, because by then the signed transaction has expired. So sol
10229
+ * settles optimistically at submit, and this loop only fetches the result.
10230
+ * The cost is recorded by the caller at POST, never here — recording it on
10231
+ * completion would lose the charge whenever a paid job then fails.
10232
+ * 2. **Every poll re-signs.** One authorization cannot be replayed across a
10233
+ * long render. The gateway binds the job to the payer address rather than
10234
+ * to the signature, so a fresh signature from the same wallet is accepted
10235
+ * and is never charged again.
10236
+ */
10237
+ async followSolanaJob(submitBody, budgetMs, intervalMs = 2e3) {
10238
+ const id = submitBody.id;
10239
+ const pollPath = submitBody.poll_url;
10240
+ if (!id || !pollPath) return submitBody;
10241
+ const pollUrl = pollPath.startsWith("http") ? pollPath : `${this.apiUrl}${pollPath.startsWith("/") ? "" : "/"}${pollPath}`;
10242
+ const deadline = Date.now() + budgetMs;
10243
+ let lastStatus = submitBody.status || "queued";
10244
+ while (Date.now() < deadline) {
10245
+ await new Promise((r) => setTimeout(r, intervalMs));
10246
+ const challenge = await this.fetchWithTimeout(pollUrl, {
10247
+ method: "GET",
10248
+ headers: { "User-Agent": USER_AGENT }
10249
+ });
10250
+ if (challenge.status === 200) {
10251
+ const done = await challenge.json();
10252
+ return done;
10253
+ }
10254
+ if (challenge.status !== 402) {
10255
+ let errorBody;
10256
+ try {
10257
+ errorBody = await challenge.json();
10258
+ } catch {
10259
+ errorBody = { error: "Poll failed" };
10260
+ }
10261
+ throw new APIError(`Poll failed: ${challenge.status}`, challenge.status, sanitizeErrorResponse(errorBody));
10262
+ }
10263
+ const { paymentPayload } = await this.signPaymentFrom402(
10264
+ pollUrl,
10265
+ challenge,
10266
+ true,
10267
+ // always a fresh blockhash; the last one is stale by now
10268
+ pollUrl
10269
+ );
10270
+ const paid = await this.fetchWithTimeout(pollUrl, {
10271
+ method: "GET",
10272
+ headers: { "User-Agent": USER_AGENT, "PAYMENT-SIGNATURE": paymentPayload }
10273
+ });
10274
+ let data = {};
10275
+ try {
10276
+ data = await paid.json();
10277
+ } catch {
10278
+ }
10279
+ lastStatus = data.status || lastStatus;
10280
+ if (lastStatus === "failed") {
10281
+ throw new APIError(
10282
+ `Upstream job failed: ${data.error || "unknown"}`,
10283
+ paid.status,
10284
+ sanitizeErrorResponse(data)
10285
+ );
10286
+ }
10287
+ if (paid.status === 200 && lastStatus === "completed") {
10288
+ return data;
10289
+ }
10290
+ if (paid.status !== 200 && paid.status !== 202 && paid.status !== 504) {
10291
+ throw new APIError(`Poll failed: ${paid.status}`, paid.status, sanitizeErrorResponse(data));
10292
+ }
10293
+ }
10294
+ throw new APIError(
10295
+ `Job did not complete within ${Math.round(budgetMs / 1e3)}s (last status: ${lastStatus}). No payment was taken.`,
10296
+ 504,
10297
+ { id, last_status: lastStatus }
10298
+ );
10299
+ }
10199
10300
  async signPaymentFrom402(url, response, forceFreshBlockhash, resourceFallback = url) {
10200
10301
  let paymentHeader = response.headers.get("payment-required");
10201
10302
  if (!paymentHeader) {
@@ -10306,6 +10407,11 @@ var SolanaLLMClient = class {
10306
10407
  body: JSON.stringify(body)
10307
10408
  });
10308
10409
  await this.assertPaid(retryResponse);
10410
+ if (retryResponse.status === 202) {
10411
+ this.recordSettlement(costUsd);
10412
+ const submitted = await retryResponse.json();
10413
+ return this.followSolanaJob(submitted, this.timeout);
10414
+ }
10309
10415
  this.recordSettlement(costUsd);
10310
10416
  return retryResponse.json();
10311
10417
  }
package/dist/index.d.cts CHANGED
@@ -2264,6 +2264,15 @@ declare class BlockrunClient {
2264
2264
  * deadline exceeded). Settlement happens only when upstream returns 200 +
2265
2265
  * completed — upstream failure or caller giving up = no charge.
2266
2266
  *
2267
+ * **EVM only, and enforced upstream.** Replaying one signature works because
2268
+ * an EIP-3009 authorization stays valid for as long as it was signed for. A
2269
+ * Solana payment is a transaction pinned to a recent blockhash and expires in
2270
+ * ~150 blocks (~60s), so replaying one could not cover a long render. No
2271
+ * guard is needed here: `signFrom402` already refuses a `solana:` network
2272
+ * outright, so this loop is unreachable on Solana. Use `SolanaLLMClient`,
2273
+ * whose poll loop signs afresh each time; the gateway binds a job to the
2274
+ * payer address rather than to the signature, and still settles exactly once.
2275
+ *
2267
2276
  * If the gateway returns 200 directly on submit (no async surface), this
2268
2277
  * short-circuits and returns the body. Most long-running endpoints (image,
2269
2278
  * video, music, voice) return 202 with a poll_url.
@@ -2768,6 +2777,18 @@ declare class SolanaLLMClient {
2768
2777
  */
2769
2778
  getBalance(): Promise<number>;
2770
2779
  /** Edit an image using img2img (Solana payment). */
2780
+ /**
2781
+ * Generate an image on Solana.
2782
+ *
2783
+ * Slow models answer 202 `{ id, poll_url }`; `requestWithPaymentRaw` follows
2784
+ * that to completion, re-signing per poll, and settles once at the end. Fast
2785
+ * models answer 200 inline and settle there.
2786
+ */
2787
+ image(prompt: string, options?: {
2788
+ model?: string;
2789
+ size?: string;
2790
+ n?: number;
2791
+ } & Record<string, unknown>): Promise<ImageResponse>;
2771
2792
  imageEdit(prompt: string, image: string | string[], options?: ImageEditOptions): Promise<ImageResponse>;
2772
2793
  /** Standalone search (Solana payment). */
2773
2794
  search(query: string, options?: SearchOptions): Promise<SearchResult>;
@@ -2897,6 +2918,24 @@ declare class SolanaLLMClient {
2897
2918
  * @returns the header value to replay with, and what it will settle for.
2898
2919
  * @throws PaymentError when the 402 carries no usable Solana requirements.
2899
2920
  */
2921
+ /**
2922
+ * Follow a 202 `{ id, poll_url }` to completion, on Solana.
2923
+ *
2924
+ * Two things differ from Base and both come from the same constraint — a
2925
+ * Solana payment is a transaction pinned to a recent blockhash, valid for
2926
+ * ~150 blocks (~60s), and a long render outlives it:
2927
+ *
2928
+ * 1. **The POST already settled.** Base settles on the completed poll; Solana
2929
+ * cannot, because by then the signed transaction has expired. So sol
2930
+ * settles optimistically at submit, and this loop only fetches the result.
2931
+ * The cost is recorded by the caller at POST, never here — recording it on
2932
+ * completion would lose the charge whenever a paid job then fails.
2933
+ * 2. **Every poll re-signs.** One authorization cannot be replayed across a
2934
+ * long render. The gateway binds the job to the payer address rather than
2935
+ * to the signature, so a fresh signature from the same wallet is accepted
2936
+ * and is never charged again.
2937
+ */
2938
+ private followSolanaJob;
2900
2939
  private signPaymentFrom402;
2901
2940
  private handlePaymentAndRetry;
2902
2941
  private requestWithPaymentRaw;
package/dist/index.d.ts CHANGED
@@ -2264,6 +2264,15 @@ declare class BlockrunClient {
2264
2264
  * deadline exceeded). Settlement happens only when upstream returns 200 +
2265
2265
  * completed — upstream failure or caller giving up = no charge.
2266
2266
  *
2267
+ * **EVM only, and enforced upstream.** Replaying one signature works because
2268
+ * an EIP-3009 authorization stays valid for as long as it was signed for. A
2269
+ * Solana payment is a transaction pinned to a recent blockhash and expires in
2270
+ * ~150 blocks (~60s), so replaying one could not cover a long render. No
2271
+ * guard is needed here: `signFrom402` already refuses a `solana:` network
2272
+ * outright, so this loop is unreachable on Solana. Use `SolanaLLMClient`,
2273
+ * whose poll loop signs afresh each time; the gateway binds a job to the
2274
+ * payer address rather than to the signature, and still settles exactly once.
2275
+ *
2267
2276
  * If the gateway returns 200 directly on submit (no async surface), this
2268
2277
  * short-circuits and returns the body. Most long-running endpoints (image,
2269
2278
  * video, music, voice) return 202 with a poll_url.
@@ -2768,6 +2777,18 @@ declare class SolanaLLMClient {
2768
2777
  */
2769
2778
  getBalance(): Promise<number>;
2770
2779
  /** Edit an image using img2img (Solana payment). */
2780
+ /**
2781
+ * Generate an image on Solana.
2782
+ *
2783
+ * Slow models answer 202 `{ id, poll_url }`; `requestWithPaymentRaw` follows
2784
+ * that to completion, re-signing per poll, and settles once at the end. Fast
2785
+ * models answer 200 inline and settle there.
2786
+ */
2787
+ image(prompt: string, options?: {
2788
+ model?: string;
2789
+ size?: string;
2790
+ n?: number;
2791
+ } & Record<string, unknown>): Promise<ImageResponse>;
2771
2792
  imageEdit(prompt: string, image: string | string[], options?: ImageEditOptions): Promise<ImageResponse>;
2772
2793
  /** Standalone search (Solana payment). */
2773
2794
  search(query: string, options?: SearchOptions): Promise<SearchResult>;
@@ -2897,6 +2918,24 @@ declare class SolanaLLMClient {
2897
2918
  * @returns the header value to replay with, and what it will settle for.
2898
2919
  * @throws PaymentError when the 402 carries no usable Solana requirements.
2899
2920
  */
2921
+ /**
2922
+ * Follow a 202 `{ id, poll_url }` to completion, on Solana.
2923
+ *
2924
+ * Two things differ from Base and both come from the same constraint — a
2925
+ * Solana payment is a transaction pinned to a recent blockhash, valid for
2926
+ * ~150 blocks (~60s), and a long render outlives it:
2927
+ *
2928
+ * 1. **The POST already settled.** Base settles on the completed poll; Solana
2929
+ * cannot, because by then the signed transaction has expired. So sol
2930
+ * settles optimistically at submit, and this loop only fetches the result.
2931
+ * The cost is recorded by the caller at POST, never here — recording it on
2932
+ * completion would lose the charge whenever a paid job then fails.
2933
+ * 2. **Every poll re-signs.** One authorization cannot be replayed across a
2934
+ * long render. The gateway binds the job to the payer address rather than
2935
+ * to the signature, so a fresh signature from the same wallet is accepted
2936
+ * and is never charged again.
2937
+ */
2938
+ private followSolanaJob;
2900
2939
  private signPaymentFrom402;
2901
2940
  private handlePaymentAndRetry;
2902
2941
  private requestWithPaymentRaw;
package/dist/index.js CHANGED
@@ -4539,7 +4539,7 @@ function getCostSummary() {
4539
4539
  }
4540
4540
 
4541
4541
  // src/version.ts
4542
- var SDK_VERSION = "3.16.0";
4542
+ var SDK_VERSION = "3.17.1";
4543
4543
  var USER_AGENT = `blockrun-ts/${SDK_VERSION}`;
4544
4544
 
4545
4545
  // src/client.ts
@@ -8594,6 +8594,15 @@ var BlockrunClient = class {
8594
8594
  * deadline exceeded). Settlement happens only when upstream returns 200 +
8595
8595
  * completed — upstream failure or caller giving up = no charge.
8596
8596
  *
8597
+ * **EVM only, and enforced upstream.** Replaying one signature works because
8598
+ * an EIP-3009 authorization stays valid for as long as it was signed for. A
8599
+ * Solana payment is a transaction pinned to a recent blockhash and expires in
8600
+ * ~150 blocks (~60s), so replaying one could not cover a long render. No
8601
+ * guard is needed here: `signFrom402` already refuses a `solana:` network
8602
+ * outright, so this loop is unreachable on Solana. Use `SolanaLLMClient`,
8603
+ * whose poll loop signs afresh each time; the gateway binds a job to the
8604
+ * payer address rather than to the signature, and still settles exactly once.
8605
+ *
8597
8606
  * If the gateway returns 200 directly on submit (no async surface), this
8598
8607
  * short-circuits and returns the body. Most long-running endpoints (image,
8599
8608
  * video, music, voice) return 202 with a poll_url.
@@ -9828,6 +9837,18 @@ var SolanaLLMClient = class {
9828
9837
  }
9829
9838
  }
9830
9839
  /** Edit an image using img2img (Solana payment). */
9840
+ /**
9841
+ * Generate an image on Solana.
9842
+ *
9843
+ * Slow models answer 202 `{ id, poll_url }`; `requestWithPaymentRaw` follows
9844
+ * that to completion, re-signing per poll, and settles once at the end. Fast
9845
+ * models answer 200 inline and settle there.
9846
+ */
9847
+ async image(prompt, options = {}) {
9848
+ const body = { prompt, ...options };
9849
+ const data = await this.requestWithPaymentRaw("/v1/images/generations", body);
9850
+ return data;
9851
+ }
9831
9852
  async imageEdit(prompt, image, options) {
9832
9853
  const body = {
9833
9854
  model: options?.model || "openai/gpt-image-2",
@@ -10075,6 +10096,86 @@ var SolanaLLMClient = class {
10075
10096
  * @returns the header value to replay with, and what it will settle for.
10076
10097
  * @throws PaymentError when the 402 carries no usable Solana requirements.
10077
10098
  */
10099
+ /**
10100
+ * Follow a 202 `{ id, poll_url }` to completion, on Solana.
10101
+ *
10102
+ * Two things differ from Base and both come from the same constraint — a
10103
+ * Solana payment is a transaction pinned to a recent blockhash, valid for
10104
+ * ~150 blocks (~60s), and a long render outlives it:
10105
+ *
10106
+ * 1. **The POST already settled.** Base settles on the completed poll; Solana
10107
+ * cannot, because by then the signed transaction has expired. So sol
10108
+ * settles optimistically at submit, and this loop only fetches the result.
10109
+ * The cost is recorded by the caller at POST, never here — recording it on
10110
+ * completion would lose the charge whenever a paid job then fails.
10111
+ * 2. **Every poll re-signs.** One authorization cannot be replayed across a
10112
+ * long render. The gateway binds the job to the payer address rather than
10113
+ * to the signature, so a fresh signature from the same wallet is accepted
10114
+ * and is never charged again.
10115
+ */
10116
+ async followSolanaJob(submitBody, budgetMs, intervalMs = 2e3) {
10117
+ const id = submitBody.id;
10118
+ const pollPath = submitBody.poll_url;
10119
+ if (!id || !pollPath) return submitBody;
10120
+ const pollUrl = pollPath.startsWith("http") ? pollPath : `${this.apiUrl}${pollPath.startsWith("/") ? "" : "/"}${pollPath}`;
10121
+ const deadline = Date.now() + budgetMs;
10122
+ let lastStatus = submitBody.status || "queued";
10123
+ while (Date.now() < deadline) {
10124
+ await new Promise((r) => setTimeout(r, intervalMs));
10125
+ const challenge = await this.fetchWithTimeout(pollUrl, {
10126
+ method: "GET",
10127
+ headers: { "User-Agent": USER_AGENT }
10128
+ });
10129
+ if (challenge.status === 200) {
10130
+ const done = await challenge.json();
10131
+ return done;
10132
+ }
10133
+ if (challenge.status !== 402) {
10134
+ let errorBody;
10135
+ try {
10136
+ errorBody = await challenge.json();
10137
+ } catch {
10138
+ errorBody = { error: "Poll failed" };
10139
+ }
10140
+ throw new APIError(`Poll failed: ${challenge.status}`, challenge.status, sanitizeErrorResponse(errorBody));
10141
+ }
10142
+ const { paymentPayload } = await this.signPaymentFrom402(
10143
+ pollUrl,
10144
+ challenge,
10145
+ true,
10146
+ // always a fresh blockhash; the last one is stale by now
10147
+ pollUrl
10148
+ );
10149
+ const paid = await this.fetchWithTimeout(pollUrl, {
10150
+ method: "GET",
10151
+ headers: { "User-Agent": USER_AGENT, "PAYMENT-SIGNATURE": paymentPayload }
10152
+ });
10153
+ let data = {};
10154
+ try {
10155
+ data = await paid.json();
10156
+ } catch {
10157
+ }
10158
+ lastStatus = data.status || lastStatus;
10159
+ if (lastStatus === "failed") {
10160
+ throw new APIError(
10161
+ `Upstream job failed: ${data.error || "unknown"}`,
10162
+ paid.status,
10163
+ sanitizeErrorResponse(data)
10164
+ );
10165
+ }
10166
+ if (paid.status === 200 && lastStatus === "completed") {
10167
+ return data;
10168
+ }
10169
+ if (paid.status !== 200 && paid.status !== 202 && paid.status !== 504) {
10170
+ throw new APIError(`Poll failed: ${paid.status}`, paid.status, sanitizeErrorResponse(data));
10171
+ }
10172
+ }
10173
+ throw new APIError(
10174
+ `Job did not complete within ${Math.round(budgetMs / 1e3)}s (last status: ${lastStatus}). No payment was taken.`,
10175
+ 504,
10176
+ { id, last_status: lastStatus }
10177
+ );
10178
+ }
10078
10179
  async signPaymentFrom402(url, response, forceFreshBlockhash, resourceFallback = url) {
10079
10180
  let paymentHeader = response.headers.get("payment-required");
10080
10181
  if (!paymentHeader) {
@@ -10185,6 +10286,11 @@ var SolanaLLMClient = class {
10185
10286
  body: JSON.stringify(body)
10186
10287
  });
10187
10288
  await this.assertPaid(retryResponse);
10289
+ if (retryResponse.status === 202) {
10290
+ this.recordSettlement(costUsd);
10291
+ const submitted = await retryResponse.json();
10292
+ return this.followSolanaJob(submitted, this.timeout);
10293
+ }
10188
10294
  this.recordSettlement(costUsd);
10189
10295
  return retryResponse.json();
10190
10296
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/llm",
3
- "version": "3.16.0",
3
+ "version": "3.17.1",
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",