@blockrun/llm 3.21.0 → 3.22.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 +70 -12
- package/dist/index.cjs +495 -99
- package/dist/index.d.cts +215 -17
- package/dist/index.d.ts +215 -17
- package/dist/index.js +491 -99
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -313,6 +313,130 @@ var ApiKeyAuth = class {
|
|
|
313
313
|
}
|
|
314
314
|
};
|
|
315
315
|
|
|
316
|
+
// src/search-wire.ts
|
|
317
|
+
function put(out, key, value) {
|
|
318
|
+
if (value !== void 0) out[key] = value;
|
|
319
|
+
}
|
|
320
|
+
function handles(list) {
|
|
321
|
+
return list?.map((h) => h.trim().replace(/^@+/, ""));
|
|
322
|
+
}
|
|
323
|
+
function sourceToWire(source) {
|
|
324
|
+
const raw = source;
|
|
325
|
+
const out = { ...raw };
|
|
326
|
+
for (const key of Object.keys(out)) if (/[A-Z]/.test(key)) delete out[key];
|
|
327
|
+
switch (source.type) {
|
|
328
|
+
case "x":
|
|
329
|
+
put(out, "included_x_handles", handles(source.includedXHandles) ?? out.included_x_handles);
|
|
330
|
+
put(out, "excluded_x_handles", handles(source.excludedXHandles) ?? out.excluded_x_handles);
|
|
331
|
+
put(out, "post_favorite_count", source.postFavoriteCount);
|
|
332
|
+
put(out, "post_view_count", source.postViewCount);
|
|
333
|
+
put(out, "enable_image_understanding", source.enableImageUnderstanding);
|
|
334
|
+
put(out, "enable_video_understanding", source.enableVideoUnderstanding);
|
|
335
|
+
break;
|
|
336
|
+
case "web":
|
|
337
|
+
put(out, "country", source.country);
|
|
338
|
+
put(out, "allowed_websites", source.allowedWebsites);
|
|
339
|
+
put(out, "excluded_websites", source.excludedWebsites);
|
|
340
|
+
put(out, "safe_search", source.safeSearch);
|
|
341
|
+
put(out, "enable_image_understanding", source.enableImageUnderstanding);
|
|
342
|
+
break;
|
|
343
|
+
case "news":
|
|
344
|
+
put(out, "country", source.country);
|
|
345
|
+
put(out, "allowed_websites", source.allowedWebsites);
|
|
346
|
+
put(out, "excluded_websites", source.excludedWebsites);
|
|
347
|
+
put(out, "safe_search", source.safeSearch);
|
|
348
|
+
break;
|
|
349
|
+
case "rss":
|
|
350
|
+
put(out, "links", source.links);
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
return out;
|
|
354
|
+
}
|
|
355
|
+
function toWireSearchParameters(sp) {
|
|
356
|
+
const raw = sp;
|
|
357
|
+
const out = {};
|
|
358
|
+
for (const [key, value] of Object.entries(raw)) if (!/[A-Z]/.test(key) && key !== "sources") out[key] = value;
|
|
359
|
+
put(out, "mode", sp.mode);
|
|
360
|
+
if (sp.sources) out.sources = sp.sources.map(sourceToWire);
|
|
361
|
+
put(out, "return_citations", sp.returnCitations);
|
|
362
|
+
put(out, "from_date", sp.fromDate);
|
|
363
|
+
put(out, "to_date", sp.toDate);
|
|
364
|
+
put(out, "max_search_results", sp.maxSearchResults);
|
|
365
|
+
put(out, "max_turns", sp.maxTurns);
|
|
366
|
+
return out;
|
|
367
|
+
}
|
|
368
|
+
function toWireTools(tools) {
|
|
369
|
+
return tools.map((tool) => {
|
|
370
|
+
if (tool.type === "x_search") {
|
|
371
|
+
const out = { type: "x_search" };
|
|
372
|
+
put(out, "allowed_x_handles", handles(tool.allowedXHandles));
|
|
373
|
+
put(out, "excluded_x_handles", handles(tool.excludedXHandles));
|
|
374
|
+
put(out, "from_date", tool.fromDate);
|
|
375
|
+
put(out, "to_date", tool.toDate);
|
|
376
|
+
put(out, "enable_image_understanding", tool.enableImageUnderstanding);
|
|
377
|
+
put(out, "enable_video_understanding", tool.enableVideoUnderstanding);
|
|
378
|
+
return out;
|
|
379
|
+
}
|
|
380
|
+
if (tool.type === "web_search") {
|
|
381
|
+
const out = { type: "web_search" };
|
|
382
|
+
put(out, "allowed_domains", tool.allowedDomains);
|
|
383
|
+
put(out, "excluded_domains", tool.excludedDomains);
|
|
384
|
+
put(out, "enable_image_understanding", tool.enableImageUnderstanding);
|
|
385
|
+
return out;
|
|
386
|
+
}
|
|
387
|
+
return tool;
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// src/receipt.ts
|
|
392
|
+
function atomicToUsd(amount) {
|
|
393
|
+
return typeof amount === "string" && /^\d+$/.test(amount) ? Number(amount) / 1e6 : void 0;
|
|
394
|
+
}
|
|
395
|
+
function receiptChargedUsd(receipt) {
|
|
396
|
+
return atomicToUsd(receipt?.extra?.chargedAmount) ?? atomicToUsd(receipt?.amount);
|
|
397
|
+
}
|
|
398
|
+
function decode(header) {
|
|
399
|
+
try {
|
|
400
|
+
const json = typeof Buffer !== "undefined" ? Buffer.from(header, "base64").toString("utf8") : atob(header);
|
|
401
|
+
const parsed = JSON.parse(json);
|
|
402
|
+
return parsed && typeof parsed === "object" ? parsed : void 0;
|
|
403
|
+
} catch {
|
|
404
|
+
return void 0;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
function readSettlement(response, quotedUsd, scheme) {
|
|
408
|
+
const header = response.headers.get("PAYMENT-RESPONSE") ?? response.headers.get("X-PAYMENT-RESPONSE");
|
|
409
|
+
const receipt = header ? decode(header) : void 0;
|
|
410
|
+
const extra = receipt?.extra;
|
|
411
|
+
const costUsd = receiptChargedUsd({
|
|
412
|
+
amount: receipt?.amount,
|
|
413
|
+
extra: extra && typeof extra === "object" ? extra : void 0
|
|
414
|
+
}) ?? quotedUsd;
|
|
415
|
+
const receiptTx = typeof receipt?.transaction === "string" && receipt.transaction ? receipt.transaction : void 0;
|
|
416
|
+
const transaction = receiptTx ?? (response.headers.get("X-Payment-Receipt")?.trim() || void 0);
|
|
417
|
+
const settlement = {
|
|
418
|
+
scheme,
|
|
419
|
+
quotedUsd,
|
|
420
|
+
...transaction ? { transaction } : {},
|
|
421
|
+
...typeof receipt?.network === "string" ? { network: receipt.network } : {},
|
|
422
|
+
...receipt?.success === false ? { success: false } : {}
|
|
423
|
+
};
|
|
424
|
+
return { costUsd, settlement };
|
|
425
|
+
}
|
|
426
|
+
function settlementSkipped(response) {
|
|
427
|
+
return response.headers.get("X-Settlement-Skipped") === "free-fallback";
|
|
428
|
+
}
|
|
429
|
+
function withCost(body, response, bookedUsd, scheme) {
|
|
430
|
+
if (settlementSkipped(response)) {
|
|
431
|
+
body.costUsd = 0;
|
|
432
|
+
body.settlement = { ...readSettlement(response, bookedUsd, scheme).settlement, skipped: "free-fallback" };
|
|
433
|
+
return body;
|
|
434
|
+
}
|
|
435
|
+
body.costUsd = bookedUsd;
|
|
436
|
+
body.settlement = readSettlement(response, bookedUsd, scheme).settlement;
|
|
437
|
+
return body;
|
|
438
|
+
}
|
|
439
|
+
|
|
316
440
|
// src/client.ts
|
|
317
441
|
import { privateKeyToAccount } from "viem/accounts";
|
|
318
442
|
|
|
@@ -4099,6 +4223,11 @@ function errSummary(err) {
|
|
|
4099
4223
|
}
|
|
4100
4224
|
return String(err).slice(0, 100);
|
|
4101
4225
|
}
|
|
4226
|
+
function requestsSearch(options) {
|
|
4227
|
+
if (options.tools?.some((tool) => tool.type === "x_search" || tool.type === "web_search")) return true;
|
|
4228
|
+
if (options.searchParameters !== void 0) return options.searchParameters.mode !== "off";
|
|
4229
|
+
return options.search === true;
|
|
4230
|
+
}
|
|
4102
4231
|
function routingProfileForModel(model) {
|
|
4103
4232
|
return AUTO_ROUTING_PROFILES[model.toLowerCase()];
|
|
4104
4233
|
}
|
|
@@ -4126,7 +4255,7 @@ function routingText(messages) {
|
|
|
4126
4255
|
};
|
|
4127
4256
|
}
|
|
4128
4257
|
function routeWithCatalog(prompt, systemPrompt, maxOutputTokens, modelPricing, options = {}) {
|
|
4129
|
-
const tools = options.tools ?? [];
|
|
4258
|
+
const tools = (options.tools ?? []).filter((tool) => tool.type === "function");
|
|
4130
4259
|
const requiresTools = options.toolChoice === "none" ? false : options.toolChoice === "required" || typeof options.toolChoice === "object" ? true : void 0;
|
|
4131
4260
|
const decision = route(prompt, systemPrompt, maxOutputTokens, {
|
|
4132
4261
|
config: DEFAULT_ROUTING_CONFIG,
|
|
@@ -4146,6 +4275,15 @@ function routeWithCatalog(prompt, systemPrompt, maxOutputTokens, modelPricing, o
|
|
|
4146
4275
|
const resolved = !id.startsWith("free/") ? id : modelPricing.has(`nvidia/${id.slice(5)}`) ? `nvidia/${id.slice(5)}` : null;
|
|
4147
4276
|
if (resolved && !callable.includes(resolved)) callable.push(resolved);
|
|
4148
4277
|
}
|
|
4278
|
+
if (requestsSearch(options)) {
|
|
4279
|
+
const searchable = callable.filter((id) => id.startsWith("xai/"));
|
|
4280
|
+
if (searchable.length === 0) {
|
|
4281
|
+
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.`
|
|
4283
|
+
);
|
|
4284
|
+
}
|
|
4285
|
+
callable.splice(0, callable.length, ...searchable);
|
|
4286
|
+
}
|
|
4149
4287
|
const estimatedInputTokens = Math.ceil(
|
|
4150
4288
|
Math.max(options.conversationChars ?? 0, `${systemPrompt ?? ""} ${prompt}`.length) / 4
|
|
4151
4289
|
);
|
|
@@ -4564,7 +4702,7 @@ function getCostSummary() {
|
|
|
4564
4702
|
}
|
|
4565
4703
|
|
|
4566
4704
|
// src/version.ts
|
|
4567
|
-
var SDK_VERSION = "3.
|
|
4705
|
+
var SDK_VERSION = "3.22.1";
|
|
4568
4706
|
var USER_AGENT = `blockrun-ts/${SDK_VERSION}`;
|
|
4569
4707
|
|
|
4570
4708
|
// src/client.ts
|
|
@@ -4710,7 +4848,9 @@ var LLMClient = class _LLMClient {
|
|
|
4710
4848
|
options?.maxOutputTokens ?? options?.maxTokens ?? DEFAULT_MAX_TOKENS,
|
|
4711
4849
|
{
|
|
4712
4850
|
routingProfile: options?.routingProfile,
|
|
4713
|
-
requiresStructuredOutput: options?.responseFormat !== void 0
|
|
4851
|
+
requiresStructuredOutput: options?.responseFormat !== void 0,
|
|
4852
|
+
search: options?.search,
|
|
4853
|
+
searchParameters: options?.searchParameters
|
|
4714
4854
|
}
|
|
4715
4855
|
);
|
|
4716
4856
|
}
|
|
@@ -4777,6 +4917,8 @@ var LLMClient = class _LLMClient {
|
|
|
4777
4917
|
requiresStructuredOutput: options.responseFormat !== void 0,
|
|
4778
4918
|
tools: options.tools,
|
|
4779
4919
|
toolChoice: options.toolChoice,
|
|
4920
|
+
search: options.search,
|
|
4921
|
+
searchParameters: options.searchParameters,
|
|
4780
4922
|
conversationChars,
|
|
4781
4923
|
hasVision
|
|
4782
4924
|
}
|
|
@@ -4791,6 +4933,7 @@ var LLMClient = class _LLMClient {
|
|
|
4791
4933
|
toolChoice: options.toolChoice,
|
|
4792
4934
|
responseFormat: options.responseFormat,
|
|
4793
4935
|
stop: options.stop,
|
|
4936
|
+
timeout: options.timeout,
|
|
4794
4937
|
// An explicit caller-supplied chain wins over the routed one.
|
|
4795
4938
|
fallbackModels: options.fallbackModels ?? decision.fallbacks
|
|
4796
4939
|
});
|
|
@@ -4867,11 +5010,11 @@ var LLMClient = class _LLMClient {
|
|
|
4867
5010
|
if (options?.temperature !== void 0) body.temperature = options.temperature;
|
|
4868
5011
|
if (options?.topP !== void 0) body.top_p = options.topP;
|
|
4869
5012
|
if (options?.searchParameters !== void 0) {
|
|
4870
|
-
body.search_parameters = options.searchParameters;
|
|
5013
|
+
body.search_parameters = toWireSearchParameters(options.searchParameters);
|
|
4871
5014
|
} else if (options?.search === true) {
|
|
4872
5015
|
body.search_parameters = { mode: "on" };
|
|
4873
5016
|
}
|
|
4874
|
-
if (options?.tools !== void 0) body.tools = options.tools;
|
|
5017
|
+
if (options?.tools !== void 0) body.tools = toWireTools(options.tools);
|
|
4875
5018
|
if (options?.toolChoice !== void 0) body.tool_choice = options.toolChoice;
|
|
4876
5019
|
if (options?.responseFormat !== void 0) body.response_format = options.responseFormat;
|
|
4877
5020
|
if (options?.stop !== void 0) body.stop = options.stop;
|
|
@@ -4882,7 +5025,7 @@ var LLMClient = class _LLMClient {
|
|
|
4882
5025
|
for (let i = 0; i < chain.length; i++) {
|
|
4883
5026
|
const candidate = chain[i];
|
|
4884
5027
|
try {
|
|
4885
|
-
return await this.requestWithPayment("/v1/chat/completions", buildBody(candidate));
|
|
5028
|
+
return await this.requestWithPayment("/v1/chat/completions", buildBody(candidate), options?.timeout);
|
|
4886
5029
|
} catch (err) {
|
|
4887
5030
|
lastErr = err;
|
|
4888
5031
|
const next = chain[i + 1];
|
|
@@ -4953,19 +5096,19 @@ var LLMClient = class _LLMClient {
|
|
|
4953
5096
|
* is `"paid-or-in-doubt"`. With an API key the request itself is billed, so
|
|
4954
5097
|
* only the account API's explicit 4xx refusal is `"unpaid"`.
|
|
4955
5098
|
*/
|
|
4956
|
-
async requestWithPayment(endpoint, body) {
|
|
5099
|
+
async requestWithPayment(endpoint, body, timeoutMs) {
|
|
4957
5100
|
const url = `${this.apiUrl}${endpoint}`;
|
|
4958
5101
|
const unpaid = {
|
|
4959
5102
|
method: "POST",
|
|
4960
5103
|
headers: { "Content-Type": "application/json", "User-Agent": USER_AGENT },
|
|
4961
5104
|
body: JSON.stringify(body)
|
|
4962
5105
|
};
|
|
4963
|
-
const response = await this.sendUnpaid(url, unpaid);
|
|
5106
|
+
const response = await this.sendUnpaid(url, unpaid, timeoutMs);
|
|
4964
5107
|
if (response.status === 502 || response.status === 503) {
|
|
4965
5108
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
4966
|
-
const retryResp = await this.sendUnpaid(url, unpaid);
|
|
5109
|
+
const retryResp = await this.sendUnpaid(url, unpaid, timeoutMs);
|
|
4967
5110
|
if (retryResp.status !== 502 && retryResp.status !== 503) {
|
|
4968
|
-
if (retryResp.status === 402) return this.handlePaymentAndRetry(url, body, retryResp);
|
|
5111
|
+
if (retryResp.status === 402) return this.handlePaymentAndRetry(url, body, retryResp, timeoutMs);
|
|
4969
5112
|
if (!retryResp.ok) {
|
|
4970
5113
|
let errorBody;
|
|
4971
5114
|
try {
|
|
@@ -4982,7 +5125,7 @@ var LLMClient = class _LLMClient {
|
|
|
4982
5125
|
}
|
|
4983
5126
|
}
|
|
4984
5127
|
if (response.status === 402) {
|
|
4985
|
-
return this.handlePaymentAndRetry(url, body, response);
|
|
5128
|
+
return this.handlePaymentAndRetry(url, body, response, timeoutMs);
|
|
4986
5129
|
}
|
|
4987
5130
|
if (!response.ok) {
|
|
4988
5131
|
let errorBody;
|
|
@@ -5007,9 +5150,9 @@ var LLMClient = class _LLMClient {
|
|
|
5007
5150
|
* {@link RetryDisposition}: `"unpaid"` with a wallet; with an API key, see
|
|
5008
5151
|
* {@link accountErrorDisposition} (the request itself is billed).
|
|
5009
5152
|
*/
|
|
5010
|
-
async sendUnpaid(url, init) {
|
|
5153
|
+
async sendUnpaid(url, init, timeoutMs) {
|
|
5011
5154
|
try {
|
|
5012
|
-
return await this.fetchWithTimeout(url, init);
|
|
5155
|
+
return await this.fetchWithTimeout(url, init, timeoutMs);
|
|
5013
5156
|
} catch (err) {
|
|
5014
5157
|
throw withDisposition(err, this.apiAuth ? accountErrorDisposition(err) : "unpaid");
|
|
5015
5158
|
}
|
|
@@ -5017,13 +5160,22 @@ var LLMClient = class _LLMClient {
|
|
|
5017
5160
|
/**
|
|
5018
5161
|
* Parse a response to a request that carried no payment: the free tier,
|
|
5019
5162
|
* or, with an API key, the billed request itself.
|
|
5163
|
+
*
|
|
5164
|
+
* With a wallet nothing was paid, so `costUsd` is 0 (a free model). With an
|
|
5165
|
+
* API key the call is billed to the account and `costUsd` stays undefined.
|
|
5020
5166
|
*/
|
|
5021
5167
|
async parseUnpaidChatResponse(response) {
|
|
5168
|
+
let served;
|
|
5022
5169
|
try {
|
|
5023
|
-
|
|
5170
|
+
served = await this.parseChatResponse(response);
|
|
5024
5171
|
} catch (err) {
|
|
5025
5172
|
throw withDisposition(err, this.apiAuth ? "paid-or-in-doubt" : "unpaid");
|
|
5026
5173
|
}
|
|
5174
|
+
if (!this.apiAuth) {
|
|
5175
|
+
served.costUsd = 0;
|
|
5176
|
+
served.settlement = { scheme: "free", quotedUsd: 0 };
|
|
5177
|
+
}
|
|
5178
|
+
return served;
|
|
5027
5179
|
}
|
|
5028
5180
|
/**
|
|
5029
5181
|
* Handle 402 response: parse requirements, sign payment, retry.
|
|
@@ -5031,7 +5183,7 @@ var LLMClient = class _LLMClient {
|
|
|
5031
5183
|
* Anything thrown before the signed payment is sent is `"unpaid"`; anything
|
|
5032
5184
|
* after it, `"paid-or-in-doubt"` (see {@link RetryDisposition}).
|
|
5033
5185
|
*/
|
|
5034
|
-
async handlePaymentAndRetry(url, body, response) {
|
|
5186
|
+
async handlePaymentAndRetry(url, body, response, timeoutMs) {
|
|
5035
5187
|
let signed;
|
|
5036
5188
|
try {
|
|
5037
5189
|
signed = await this.signFrom402(url, response);
|
|
@@ -5039,7 +5191,7 @@ var LLMClient = class _LLMClient {
|
|
|
5039
5191
|
throw withDisposition(err, "unpaid");
|
|
5040
5192
|
}
|
|
5041
5193
|
try {
|
|
5042
|
-
return await this.sendPaid(url, body, signed.paymentPayload, signed.details);
|
|
5194
|
+
return await this.sendPaid(url, body, signed.paymentPayload, signed.details, timeoutMs);
|
|
5043
5195
|
} catch (err) {
|
|
5044
5196
|
throw withDisposition(err, "paid-or-in-doubt");
|
|
5045
5197
|
}
|
|
@@ -5084,7 +5236,7 @@ var LLMClient = class _LLMClient {
|
|
|
5084
5236
|
return { paymentPayload, details };
|
|
5085
5237
|
}
|
|
5086
5238
|
/** Send the signed payment and read the answer. The payment may settle from here on. */
|
|
5087
|
-
async sendPaid(url, body, paymentPayload, details) {
|
|
5239
|
+
async sendPaid(url, body, paymentPayload, details, timeoutMs) {
|
|
5088
5240
|
const retryResponse = await this.fetchWithTimeout(url, {
|
|
5089
5241
|
method: "POST",
|
|
5090
5242
|
headers: {
|
|
@@ -5093,7 +5245,7 @@ var LLMClient = class _LLMClient {
|
|
|
5093
5245
|
"PAYMENT-SIGNATURE": paymentPayload
|
|
5094
5246
|
},
|
|
5095
5247
|
body: JSON.stringify(body)
|
|
5096
|
-
});
|
|
5248
|
+
}, timeoutMs);
|
|
5097
5249
|
if (retryResponse.status === 502 || retryResponse.status === 503) {
|
|
5098
5250
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
5099
5251
|
const retryResp2 = await this.fetchWithTimeout(url, {
|
|
@@ -5104,7 +5256,7 @@ var LLMClient = class _LLMClient {
|
|
|
5104
5256
|
"PAYMENT-SIGNATURE": paymentPayload
|
|
5105
5257
|
},
|
|
5106
5258
|
body: JSON.stringify(body)
|
|
5107
|
-
});
|
|
5259
|
+
}, timeoutMs);
|
|
5108
5260
|
if (retryResp2.status !== 502 && retryResp2.status !== 503) {
|
|
5109
5261
|
if (retryResp2.status === 402) throw new PaymentError("Payment was rejected. Check your wallet balance.");
|
|
5110
5262
|
if (!retryResp2.ok) {
|
|
@@ -5116,11 +5268,7 @@ var LLMClient = class _LLMClient {
|
|
|
5116
5268
|
}
|
|
5117
5269
|
throw new APIError(`API error after payment: ${retryResp2.status}`, retryResp2.status, sanitizeErrorResponse(errorBody));
|
|
5118
5270
|
}
|
|
5119
|
-
|
|
5120
|
-
this.sessionCalls += 1;
|
|
5121
|
-
this.sessionTotalUsd += costUsd2;
|
|
5122
|
-
this.recordCost(url, costUsd2, { body, network: details.network });
|
|
5123
|
-
return this.parseChatResponse(retryResp2);
|
|
5271
|
+
return this.bookPaid(url, body, details, retryResp2);
|
|
5124
5272
|
}
|
|
5125
5273
|
}
|
|
5126
5274
|
if (retryResponse.status === 402) {
|
|
@@ -5139,11 +5287,22 @@ var LLMClient = class _LLMClient {
|
|
|
5139
5287
|
sanitizeErrorResponse(errorBody)
|
|
5140
5288
|
);
|
|
5141
5289
|
}
|
|
5290
|
+
return this.bookPaid(url, body, details, retryResponse);
|
|
5291
|
+
}
|
|
5292
|
+
/**
|
|
5293
|
+
* Book a served, paid chat call and attach its cost. A response the gateway
|
|
5294
|
+
* served from a free fallback with settlement skipped
|
|
5295
|
+
* (`X-Settlement-Skipped: free-fallback`) moved nothing on chain, so it is
|
|
5296
|
+
* not added to `getSpending()` or the cost log, and reports `costUsd: 0`.
|
|
5297
|
+
*/
|
|
5298
|
+
async bookPaid(url, body, details, response) {
|
|
5142
5299
|
const costUsd = parseFloat(details.amount) / 1e6;
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5300
|
+
if (!settlementSkipped(response)) {
|
|
5301
|
+
this.sessionCalls += 1;
|
|
5302
|
+
this.sessionTotalUsd += costUsd;
|
|
5303
|
+
this.recordCost(url, costUsd, { body, network: details.network });
|
|
5304
|
+
}
|
|
5305
|
+
return withCost(await this.parseChatResponse(response), response, costUsd, "exact");
|
|
5147
5306
|
}
|
|
5148
5307
|
/**
|
|
5149
5308
|
* Sign a payment header and return the PAYMENT-SIGNATURE value.
|
|
@@ -5213,7 +5372,7 @@ var LLMClient = class _LLMClient {
|
|
|
5213
5372
|
};
|
|
5214
5373
|
if (options?.temperature !== void 0) body.temperature = options.temperature;
|
|
5215
5374
|
if (options?.topP !== void 0) body.top_p = options.topP;
|
|
5216
|
-
if (options?.tools !== void 0) body.tools = options.tools;
|
|
5375
|
+
if (options?.tools !== void 0) body.tools = toWireTools(options.tools);
|
|
5217
5376
|
if (options?.toolChoice !== void 0) body.tool_choice = options.toolChoice;
|
|
5218
5377
|
if (options?.responseFormat !== void 0) body.response_format = options.responseFormat;
|
|
5219
5378
|
if (options?.stop !== void 0) body.stop = options.stop;
|
|
@@ -5611,11 +5770,11 @@ var LLMClient = class _LLMClient {
|
|
|
5611
5770
|
return retryResponse.json();
|
|
5612
5771
|
}
|
|
5613
5772
|
/**
|
|
5614
|
-
* Fetch with timeout
|
|
5773
|
+
* Fetch with timeout: `timeoutMs` (a per-call override), else the client's `timeout`.
|
|
5615
5774
|
*/
|
|
5616
|
-
async fetchWithTimeout(url, options) {
|
|
5775
|
+
async fetchWithTimeout(url, options, timeoutMs = this.timeout) {
|
|
5617
5776
|
const controller = new AbortController();
|
|
5618
|
-
const timeoutId = setTimeout(() => controller.abort(),
|
|
5777
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
5619
5778
|
try {
|
|
5620
5779
|
const response = await (this.apiAuth ? this.apiAuth.fetch.bind(this.apiAuth) : fetch)(url, {
|
|
5621
5780
|
...options,
|
|
@@ -9600,6 +9759,7 @@ import bs582 from "bs58";
|
|
|
9600
9759
|
var BATCH_SCHEME = "batch-settlement";
|
|
9601
9760
|
var BLOCKRUN_SOL_OPERATOR = "5YKPQUFjw5WQqhSUkEGKNNfYYVqnRRNbpYyL71qQ1vm3";
|
|
9602
9761
|
var DEFAULT_BATCH_RATE_LIMIT = { maxAttempts: 3, maxWaitMs: 6e4 };
|
|
9762
|
+
var DEFAULT_BATCH_IN_DOUBT = { maxWaitMs: 3e5 };
|
|
9603
9763
|
var BatchPaymentUnresolvedError = class extends PaymentError {
|
|
9604
9764
|
/** The {@link SolanaBatchEvent.reason} of the `unresolved` event reported with it. */
|
|
9605
9765
|
reason;
|
|
@@ -10276,6 +10436,46 @@ function withoutClientSignedBatch(paymentRequired) {
|
|
|
10276
10436
|
const accepts = paymentRequired.accepts ?? [];
|
|
10277
10437
|
return accepts.some(clientSignedBatch) ? { ...paymentRequired, accepts: accepts.filter((accept) => !clientSignedBatch(accept)) } : paymentRequired;
|
|
10278
10438
|
}
|
|
10439
|
+
var PAYMENT_IDENTIFIER = "payment-identifier";
|
|
10440
|
+
function withPaymentIdentifier(paymentRequired) {
|
|
10441
|
+
const declared = paymentRequired.extensions?.[PAYMENT_IDENTIFIER];
|
|
10442
|
+
if (typeof declared?.info?.required !== "boolean") return paymentRequired;
|
|
10443
|
+
const id = `pay_${randomUUID().replace(/-/g, "")}`;
|
|
10444
|
+
return {
|
|
10445
|
+
...paymentRequired,
|
|
10446
|
+
extensions: { ...paymentRequired.extensions, [PAYMENT_IDENTIFIER]: { ...declared, info: { ...declared.info, id } } }
|
|
10447
|
+
};
|
|
10448
|
+
}
|
|
10449
|
+
function peekReceipt(http, getHeader) {
|
|
10450
|
+
try {
|
|
10451
|
+
return http.getPaymentSettleResponse(getHeader);
|
|
10452
|
+
} catch {
|
|
10453
|
+
return void 0;
|
|
10454
|
+
}
|
|
10455
|
+
}
|
|
10456
|
+
function receiptSucceeded(http, answer) {
|
|
10457
|
+
return peekReceipt(http, (name) => answer.headers.get(name))?.success === true;
|
|
10458
|
+
}
|
|
10459
|
+
function hasPaymentIdentifier(payload) {
|
|
10460
|
+
const info = payload.extensions?.[PAYMENT_IDENTIFIER]?.info;
|
|
10461
|
+
return typeof info?.id === "string";
|
|
10462
|
+
}
|
|
10463
|
+
var IN_DOUBT_MIN_WAIT_MS = 1e3;
|
|
10464
|
+
var IN_DOUBT_MAX_WAIT_MS = 3e4;
|
|
10465
|
+
function replayMayResolve(doubt) {
|
|
10466
|
+
if (doubt.unanswered) return true;
|
|
10467
|
+
const answer = doubt.held;
|
|
10468
|
+
if (!answer) return false;
|
|
10469
|
+
return answer.status >= 500 || answer.status === 429 || answer.status === 409 && Boolean(answer.headers.get("retry-after")?.trim());
|
|
10470
|
+
}
|
|
10471
|
+
function inDoubtWait(doubt, replay) {
|
|
10472
|
+
const asked = doubt.held ? parseRetryAfter(doubt.held.headers.get("retry-after")) : void 0;
|
|
10473
|
+
return Math.min(IN_DOUBT_MAX_WAIT_MS, Math.max(IN_DOUBT_MIN_WAIT_MS, asked ?? backoffDelay(replay)));
|
|
10474
|
+
}
|
|
10475
|
+
function replayDoubt(outcome) {
|
|
10476
|
+
if (outcome.kind === "charged" || outcome.kind === "in_doubt") return outcome;
|
|
10477
|
+
return { kind: "in_doubt", reason: "replay_unresolved", replayable: false, settled: true, detail: "the replay was refused" };
|
|
10478
|
+
}
|
|
10279
10479
|
function trustedBatchAccepts(paymentRequired, operators) {
|
|
10280
10480
|
return (paymentRequired.accepts ?? []).filter((candidate) => {
|
|
10281
10481
|
const extra = candidate.extra;
|
|
@@ -10285,9 +10485,6 @@ function trustedBatchAccepts(paymentRequired, operators) {
|
|
|
10285
10485
|
function trustedBatchAccept(paymentRequired, operators) {
|
|
10286
10486
|
return trustedBatchAccepts(paymentRequired, operators)[0];
|
|
10287
10487
|
}
|
|
10288
|
-
function atomicToUsd(amount) {
|
|
10289
|
-
return typeof amount === "string" && /^\d+$/.test(amount) ? Number(amount) / 1e6 : void 0;
|
|
10290
|
-
}
|
|
10291
10488
|
function chargedUsd(getReceipt) {
|
|
10292
10489
|
let receipt;
|
|
10293
10490
|
try {
|
|
@@ -10295,7 +10492,7 @@ function chargedUsd(getReceipt) {
|
|
|
10295
10492
|
} catch {
|
|
10296
10493
|
return 0;
|
|
10297
10494
|
}
|
|
10298
|
-
return
|
|
10495
|
+
return receiptChargedUsd(receipt) ?? 0;
|
|
10299
10496
|
}
|
|
10300
10497
|
function rebuiltCharge(settled, payload) {
|
|
10301
10498
|
if (settled?.success !== true) return 0n;
|
|
@@ -10415,13 +10612,42 @@ function parseRetryAfter(value, now = Date.now()) {
|
|
|
10415
10612
|
}
|
|
10416
10613
|
return Number.isFinite(ms) && ms <= MAX_RETRY_AFTER_MS ? ms : void 0;
|
|
10417
10614
|
}
|
|
10418
|
-
var
|
|
10615
|
+
var NOT_BROADCAST_REFUSALS = /* @__PURE__ */ new Set([
|
|
10616
|
+
// 429
|
|
10419
10617
|
"batch_account_channel_capacity_exhausted",
|
|
10420
10618
|
"batch_channel_capacity_exhausted",
|
|
10421
|
-
"batch_deposit_rate_limited"
|
|
10619
|
+
"batch_deposit_rate_limited",
|
|
10620
|
+
"batch_deposit_ip_rate_limited",
|
|
10621
|
+
// 503
|
|
10622
|
+
"batch_signer_funding_headroom_exhausted",
|
|
10623
|
+
"batch_new_channels_disabled",
|
|
10624
|
+
"batch_public_new_channels_disabled",
|
|
10625
|
+
// 403
|
|
10626
|
+
"batch_account_admission_paused",
|
|
10627
|
+
"batch_channel_owner_mismatch",
|
|
10628
|
+
"batch_fee_payer_unavailable",
|
|
10629
|
+
"batch_fee_payer_not_dedicated",
|
|
10630
|
+
"batch_fee_payer_lane_not_authorized",
|
|
10631
|
+
"batch_transaction_fee_payer_mismatch",
|
|
10632
|
+
// 400
|
|
10633
|
+
"batch_initial_deposit_out_of_range",
|
|
10634
|
+
"batch_deposit_asset_not_supported",
|
|
10635
|
+
"batch_withdraw_delay_out_of_range"
|
|
10422
10636
|
]);
|
|
10423
10637
|
function provesNothingBroadcast(receipt) {
|
|
10424
|
-
return receipt?.success === false && !receipt.transaction && typeof receipt.errorReason === "string" &&
|
|
10638
|
+
return receipt?.success === false && !receipt.transaction && typeof receipt.errorReason === "string" && NOT_BROADCAST_REFUSALS.has(receipt.errorReason);
|
|
10639
|
+
}
|
|
10640
|
+
var CONFIG_REFUSALS = /* @__PURE__ */ new Set([
|
|
10641
|
+
"batch_initial_deposit_out_of_range",
|
|
10642
|
+
"batch_deposit_asset_not_supported",
|
|
10643
|
+
"batch_withdraw_delay_out_of_range"
|
|
10644
|
+
]);
|
|
10645
|
+
var REFUSAL_COOLDOWN_MS = 6e4;
|
|
10646
|
+
var CONFIG_REFUSAL_COOLDOWN_MS = 10 * 6e4;
|
|
10647
|
+
var REFUSAL_COOLDOWN_MAX_MS = 15 * 6e4;
|
|
10648
|
+
function refusalCooldownMs(errorReason, retryAfterMs2) {
|
|
10649
|
+
if (retryAfterMs2 !== void 0) return Math.min(retryAfterMs2, REFUSAL_COOLDOWN_MAX_MS);
|
|
10650
|
+
return errorReason !== void 0 && CONFIG_REFUSALS.has(errorReason) ? CONFIG_REFUSAL_COOLDOWN_MS : REFUSAL_COOLDOWN_MS;
|
|
10425
10651
|
}
|
|
10426
10652
|
function backoffDelay(attempt) {
|
|
10427
10653
|
const base = 1e3 * 2 ** Math.max(0, attempt - 1);
|
|
@@ -10554,7 +10780,7 @@ function intentTarget(intent) {
|
|
|
10554
10780
|
var SolanaBatchPayer = class {
|
|
10555
10781
|
constructor(init) {
|
|
10556
10782
|
this.init = init;
|
|
10557
|
-
const { operators, rateLimit } = init.options;
|
|
10783
|
+
const { operators, rateLimit, inDoubt } = init.options;
|
|
10558
10784
|
if (!Array.isArray(operators) || operators.length === 0 || operators.some((o) => typeof o !== "string" || !o)) {
|
|
10559
10785
|
throw new Error("batch.operators must list at least one base58 operator public key");
|
|
10560
10786
|
}
|
|
@@ -10566,11 +10792,16 @@ var SolanaBatchPayer = class {
|
|
|
10566
10792
|
if (!Number.isFinite(this.maxWaitMs) || this.maxWaitMs < 0) {
|
|
10567
10793
|
throw new Error("batch.rateLimit.maxWaitMs must be a finite number of ms >= 0");
|
|
10568
10794
|
}
|
|
10795
|
+
this.inDoubtMaxWaitMs = inDoubt?.maxWaitMs ?? DEFAULT_BATCH_IN_DOUBT.maxWaitMs;
|
|
10796
|
+
if (!Number.isFinite(this.inDoubtMaxWaitMs) || this.inDoubtMaxWaitMs < 0) {
|
|
10797
|
+
throw new Error("batch.inDoubt.maxWaitMs must be a finite number of ms >= 0");
|
|
10798
|
+
}
|
|
10569
10799
|
}
|
|
10570
10800
|
init;
|
|
10571
10801
|
state;
|
|
10572
10802
|
maxAttempts;
|
|
10573
10803
|
maxWaitMs;
|
|
10804
|
+
inDoubtMaxWaitMs;
|
|
10574
10805
|
counters = {
|
|
10575
10806
|
fallbacks: 0,
|
|
10576
10807
|
fallbacksByReason: {},
|
|
@@ -10939,14 +11170,19 @@ var SolanaBatchPayer = class {
|
|
|
10939
11170
|
* the call's result (`served`, nothing paid); one that fails otherwise
|
|
10940
11171
|
* is the call's error (`failed`, `"unpaid"`), never an exact payment
|
|
10941
11172
|
* against the stale challenge;
|
|
10942
|
-
* - in doubt: ends only with a definitive success receipt from
|
|
10943
|
-
*
|
|
10944
|
-
*
|
|
10945
|
-
*
|
|
10946
|
-
*
|
|
11173
|
+
* - in doubt: ends only with a definitive success receipt from a replay
|
|
11174
|
+
* of the identical payment ({@link resolveInDoubt}: the one replay a
|
|
11175
|
+
* receipt-less 429 is allowed, or the replays of a payment carrying a
|
|
11176
|
+
* payment identifier while the gateway says one can succeed, within
|
|
11177
|
+
* `inDoubt.maxWaitMs`), or a thrown {@link BatchPaymentUnresolvedError}.
|
|
11178
|
+
* No new authorization, no new deposit, no exact payment and no fallback
|
|
11179
|
+
* model is ever paid for a call in doubt.
|
|
10947
11180
|
*
|
|
10948
11181
|
* The wallet stays in cooldown after a 429, so its other calls wait
|
|
10949
|
-
* instead of sending opens of their own.
|
|
11182
|
+
* instead of sending opens of their own. After a deposit the facilitator
|
|
11183
|
+
* refused before broadcasting it (a 402 with a failed `batch_*` admission
|
|
11184
|
+
* receipt), its calls do not wait: they pay exact at once
|
|
11185
|
+
* (`admission_refused`) until the refusal cooldown ends.
|
|
10950
11186
|
*
|
|
10951
11187
|
* @param closesAtStart - {@link closeFence}, taken before the call's first
|
|
10952
11188
|
* (unpaid) request. If a close completed since, the call pays exact
|
|
@@ -11010,6 +11246,15 @@ var SolanaBatchPayer = class {
|
|
|
11010
11246
|
try {
|
|
11011
11247
|
for (; ; ) {
|
|
11012
11248
|
const now = Date.now();
|
|
11249
|
+
if ((wallet.refusedUntil ?? 0) > now) {
|
|
11250
|
+
return fallback({
|
|
11251
|
+
reason: "admission_refused",
|
|
11252
|
+
errorReason: wallet.refusedFor,
|
|
11253
|
+
retryAfterMs: (wallet.refusedUntil ?? 0) - now,
|
|
11254
|
+
attempt: budget.attempt,
|
|
11255
|
+
detail: `the facilitator refused this wallet's last batch deposit (${wallet.refusedFor ?? "unknown"}); batch is skipped until the refusal cooldown ends`
|
|
11256
|
+
});
|
|
11257
|
+
}
|
|
11013
11258
|
if (wallet.cooldownUntil > now && wallet.cooldownUntil !== waitedUntil) {
|
|
11014
11259
|
const wait = wallet.cooldownUntil - now;
|
|
11015
11260
|
if (budget.waited + wait > this.maxWaitMs) {
|
|
@@ -11099,12 +11344,23 @@ var SolanaBatchPayer = class {
|
|
|
11099
11344
|
if (built.kind !== "authorization") {
|
|
11100
11345
|
if (outcome.depositSafe) this.forgetIntent(wallet, built.key);
|
|
11101
11346
|
else this.distrust(wallet, built, "deposit_refused");
|
|
11347
|
+
if (outcome.refusedForMs !== void 0) {
|
|
11348
|
+
const until = Date.now() + outcome.refusedForMs;
|
|
11349
|
+
if (until > (wallet.refusedUntil ?? 0)) {
|
|
11350
|
+
wallet.refusedUntil = until;
|
|
11351
|
+
wallet.refusedFor = outcome.errorReason;
|
|
11352
|
+
}
|
|
11353
|
+
}
|
|
11102
11354
|
}
|
|
11103
11355
|
sent = void 0;
|
|
11104
11356
|
release();
|
|
11105
11357
|
if (!outcome.rateLimited) {
|
|
11106
|
-
const { kind: _kind, rateLimited: _limited, depositSafe: _safe, ...event } = outcome;
|
|
11107
|
-
return fallback({
|
|
11358
|
+
const { kind: _kind, rateLimited: _limited, depositSafe: _safe, refusedForMs, ...event } = outcome;
|
|
11359
|
+
return fallback({
|
|
11360
|
+
...event,
|
|
11361
|
+
...refusedForMs !== void 0 && built.kind !== "authorization" ? { retryAfterMs: refusedForMs } : {},
|
|
11362
|
+
attempt: budget.attempt
|
|
11363
|
+
});
|
|
11108
11364
|
}
|
|
11109
11365
|
const wait = this.coolDown(wallet, outcome.rateLimited.retryAfterMs ?? backoffDelay(budget.attempt));
|
|
11110
11366
|
if (budget.attempt >= this.maxAttempts || budget.waited + wait > this.maxWaitMs) {
|
|
@@ -11150,7 +11406,7 @@ var SolanaBatchPayer = class {
|
|
|
11150
11406
|
if (built.kind !== "authorization" && !(built.key && wallet.resyncs.has(built.key))) this.forgetIntent(wallet, built.key);
|
|
11151
11407
|
sent = void 0;
|
|
11152
11408
|
release();
|
|
11153
|
-
await this.report({ type: "recovered", reason: "rate_limited", attempt: budget.attempt });
|
|
11409
|
+
await this.report({ type: "recovered", reason: outcome.status === 429 ? "rate_limited" : "in_doubt", attempt: budget.attempt });
|
|
11154
11410
|
return { kind: "paid", response: resolved.response, chargedUsd: resolved.chargedUsd };
|
|
11155
11411
|
}
|
|
11156
11412
|
return await this.raiseUnresolved(wallet, built, resolved, budget.attempt);
|
|
@@ -11199,21 +11455,24 @@ var SolanaBatchPayer = class {
|
|
|
11199
11455
|
/**
|
|
11200
11456
|
* Try to end a payment in doubt with the gateway's own word.
|
|
11201
11457
|
*
|
|
11202
|
-
* The one source of evidence this SDK accepts
|
|
11203
|
-
*
|
|
11204
|
-
*
|
|
11205
|
-
*
|
|
11206
|
-
*
|
|
11207
|
-
*
|
|
11458
|
+
* The one source of evidence this SDK accepts is a definitive success
|
|
11459
|
+
* receipt on a byte-identical replay of the payment. Any other answer to a
|
|
11460
|
+
* replay (a 402 or `duplicate_settlement`, which mean the original reached
|
|
11461
|
+
* the gateway; a 409; another 429; a 5xx; a timeout) leaves it in doubt.
|
|
11462
|
+
*
|
|
11463
|
+
* - A payment carrying a payment identifier is replayed for as long as
|
|
11464
|
+
* the gateway says a later replay can succeed ({@link replayInDoubt}).
|
|
11465
|
+
* - Any other payment gets a replay only after a 429 without a receipt:
|
|
11466
|
+
* one, after its backoff (the owner's bounded safety attempt), within
|
|
11467
|
+
* `rateLimit`. Every other in-doubt outcome is never replayed.
|
|
11208
11468
|
*
|
|
11209
11469
|
* Chain state is not evidence here: it can settle a deposit (see
|
|
11210
|
-
* `resync`), never whether the gateway charged.
|
|
11211
|
-
* receipt on every response, a request-status endpoint with a fence)
|
|
11212
|
-
* plugs in here, before the caller raises.
|
|
11470
|
+
* `resync`), never whether the gateway charged.
|
|
11213
11471
|
*
|
|
11214
11472
|
* @returns the charged replay, or the doubt to raise.
|
|
11215
11473
|
*/
|
|
11216
11474
|
async resolveInDoubt(wallet, sent, doubt, send, budget) {
|
|
11475
|
+
if (hasPaymentIdentifier(sent.payload)) return this.replayInDoubt(wallet, sent, doubt, send, budget.attempt);
|
|
11217
11476
|
if (!doubt.replayable) return doubt;
|
|
11218
11477
|
const wait = this.coolDown(wallet, doubt.retryAfterMs ?? backoffDelay(budget.attempt));
|
|
11219
11478
|
if (budget.attempt >= this.maxAttempts || budget.waited + wait > this.maxWaitMs) {
|
|
@@ -11233,9 +11492,94 @@ var SolanaBatchPayer = class {
|
|
|
11233
11492
|
budget.waited += wait;
|
|
11234
11493
|
await sleep2(wait);
|
|
11235
11494
|
budget.attempt += 1;
|
|
11236
|
-
|
|
11237
|
-
|
|
11238
|
-
|
|
11495
|
+
return replayDoubt(await this.sendOnce(wallet, sent, send, true, true));
|
|
11496
|
+
}
|
|
11497
|
+
/**
|
|
11498
|
+
* Replay a payment in doubt that carries a payment identifier until the
|
|
11499
|
+
* gateway's answer settles it.
|
|
11500
|
+
*
|
|
11501
|
+
* A gateway that declares `payment-identifier` stores each paid response
|
|
11502
|
+
* under its identifier and answers a byte-identical replay of a completed
|
|
11503
|
+
* request with it, the original receipt included; while the original is
|
|
11504
|
+
* still running, it answers 409 with a `Retry-After`. So the identical
|
|
11505
|
+
* payment (the same `PAYMENT-SIGNATURE` header and the same body) is
|
|
11506
|
+
* replayed for as long as the last answer says a later one can succeed
|
|
11507
|
+
* ({@link replayMayResolve}), after {@link inDoubtWait} each time, as long
|
|
11508
|
+
* as the wait fits in `inDoubt.maxWaitMs`, counted from the first doubt:
|
|
11509
|
+
* every wait (as long as it really took) and every replay's round trip. A
|
|
11510
|
+
* replay already sent is not cut short. These replays do not count
|
|
11511
|
+
* against `rateLimit`; a 429 among them still puts the wallet in cooldown.
|
|
11512
|
+
*
|
|
11513
|
+
* Every answer that does not settle it is held back from the scheme
|
|
11514
|
+
* ({@link InDoubt.held}) and handed over only once no replay follows, so
|
|
11515
|
+
* the scheme rolls the payment back once, or takes the receipt that answer
|
|
11516
|
+
* carries. That is the last answer, unless an earlier one carried a
|
|
11517
|
+
* success receipt and the later ones do not: a success receipt is the
|
|
11518
|
+
* gateway's word that it charged, and handing the scheme a receipt-less
|
|
11519
|
+
* refusal of a replay instead would roll a real charge back and desync the
|
|
11520
|
+
* channel's cumulative.
|
|
11521
|
+
*
|
|
11522
|
+
* @returns the charged replay, or the doubt to raise.
|
|
11523
|
+
*/
|
|
11524
|
+
async replayInDoubt(wallet, sent, first, send, attempt) {
|
|
11525
|
+
let doubt = first;
|
|
11526
|
+
let held = first.held;
|
|
11527
|
+
let heldSucceeded = held !== void 0 && receiptSucceeded(sent.http, held);
|
|
11528
|
+
let replays = 0;
|
|
11529
|
+
let spent = 0;
|
|
11530
|
+
const stop = async (note) => {
|
|
11531
|
+
if (!doubt.settled && held) {
|
|
11532
|
+
const answer = held;
|
|
11533
|
+
await this.settle(sent.http, sent.payload, (name) => answer.headers.get(name), answer.status);
|
|
11534
|
+
}
|
|
11535
|
+
const notes = [replays > 0 ? `${replays} replay${replays === 1 ? "" : "s"} did not resolve it` : "", note ?? ""];
|
|
11536
|
+
return {
|
|
11537
|
+
...doubt,
|
|
11538
|
+
settled: doubt.settled || held !== void 0,
|
|
11539
|
+
held: void 0,
|
|
11540
|
+
firstReason: first.reason,
|
|
11541
|
+
detail: [doubt.detail, ...notes].filter(Boolean).join("; ")
|
|
11542
|
+
};
|
|
11543
|
+
};
|
|
11544
|
+
for (; ; ) {
|
|
11545
|
+
if (!replayMayResolve(doubt)) {
|
|
11546
|
+
return stop(doubt.held?.status === 409 ? "the gateway sent no Retry-After, so no replay can resolve it" : void 0);
|
|
11547
|
+
}
|
|
11548
|
+
const wait = inDoubtWait(doubt, replays + 1);
|
|
11549
|
+
const status2 = doubt.held?.status;
|
|
11550
|
+
if (status2 === 429) this.coolDown(wallet, (doubt.held && parseRetryAfter(doubt.held.headers.get("retry-after"))) ?? wait);
|
|
11551
|
+
if (spent + wait > this.inDoubtMaxWaitMs) {
|
|
11552
|
+
return stop(`batch.inDoubt.maxWaitMs (${this.inDoubtMaxWaitMs} ms) leaves no room for ${replays > 0 ? "another replay" : "a replay"}`);
|
|
11553
|
+
}
|
|
11554
|
+
await this.report({
|
|
11555
|
+
type: "backoff",
|
|
11556
|
+
reason: status2 === 429 ? "rate_limited" : "in_doubt",
|
|
11557
|
+
status: status2,
|
|
11558
|
+
errorReason: doubt.errorReason,
|
|
11559
|
+
retryAfterMs: wait,
|
|
11560
|
+
attempt,
|
|
11561
|
+
detail: `replay ${replays + 1} of the payment in doubt`
|
|
11562
|
+
});
|
|
11563
|
+
const sleptFrom = Date.now();
|
|
11564
|
+
await sleep2(wait);
|
|
11565
|
+
spent += Math.max(wait, Date.now() - sleptFrom);
|
|
11566
|
+
replays += 1;
|
|
11567
|
+
const startedAt = Date.now();
|
|
11568
|
+
const replayed = replayDoubt(await this.sendOnce(wallet, sent, send, true, true));
|
|
11569
|
+
spent += Math.max(0, Date.now() - startedAt);
|
|
11570
|
+
if (replayed.kind === "charged") return replayed;
|
|
11571
|
+
doubt = replayed;
|
|
11572
|
+
if (doubt.held) {
|
|
11573
|
+
const succeeded = receiptSucceeded(sent.http, doubt.held);
|
|
11574
|
+
if (succeeded || !heldSucceeded) {
|
|
11575
|
+
held = doubt.held;
|
|
11576
|
+
heldSucceeded = succeeded;
|
|
11577
|
+
}
|
|
11578
|
+
} else if (doubt.settled) {
|
|
11579
|
+
held = void 0;
|
|
11580
|
+
heldSucceeded = false;
|
|
11581
|
+
}
|
|
11582
|
+
}
|
|
11239
11583
|
}
|
|
11240
11584
|
/**
|
|
11241
11585
|
* Raise a payment in doubt: release the scheme's hold on it (the channel
|
|
@@ -11246,10 +11590,11 @@ var SolanaBatchPayer = class {
|
|
|
11246
11590
|
async raiseUnresolved(wallet, sent, doubt, attempt) {
|
|
11247
11591
|
if (!doubt.settled) await this.settle(sent.http, sent.payload, () => null, 0);
|
|
11248
11592
|
if (sent.kind !== "authorization") {
|
|
11593
|
+
const reason = doubt.firstReason ?? doubt.reason;
|
|
11249
11594
|
this.distrust(
|
|
11250
11595
|
wallet,
|
|
11251
11596
|
sent,
|
|
11252
|
-
|
|
11597
|
+
reason === "no_response" ? "deposit_unanswered" : reason === "replay_unresolved" ? "deposit_rate_limited" : "deposit_failed"
|
|
11253
11598
|
);
|
|
11254
11599
|
}
|
|
11255
11600
|
await this.report({
|
|
@@ -11336,8 +11681,8 @@ var SolanaBatchPayer = class {
|
|
|
11336
11681
|
* here, and only for a first send.
|
|
11337
11682
|
*
|
|
11338
11683
|
* @param retry - count this send in `retries` (it follows this call's own 429).
|
|
11339
|
-
* @param replay - this is
|
|
11340
|
-
*
|
|
11684
|
+
* @param replay - this is a replay of a payment in doubt: only a 2xx with
|
|
11685
|
+
* a success receipt counts, everything else stays in doubt.
|
|
11341
11686
|
*/
|
|
11342
11687
|
async sendOnce(wallet, sent, send, retry, replay) {
|
|
11343
11688
|
if (retry) this.counters.retries += 1;
|
|
@@ -11351,6 +11696,7 @@ var SolanaBatchPayer = class {
|
|
|
11351
11696
|
reason: replay ? "replay_unresolved" : "no_response",
|
|
11352
11697
|
replayable: false,
|
|
11353
11698
|
settled: false,
|
|
11699
|
+
unanswered: true,
|
|
11354
11700
|
detail: `${replay ? "the replay" : "the payment"} got no answer (${err instanceof Error ? `${err.name}: ${err.message}` : String(err)})`,
|
|
11355
11701
|
cause: err
|
|
11356
11702
|
};
|
|
@@ -11375,6 +11721,7 @@ var SolanaBatchPayer = class {
|
|
|
11375
11721
|
const getHeader = (name) => response.headers.get(name);
|
|
11376
11722
|
const receipted = Boolean(getHeader("PAYMENT-RESPONSE") || getHeader("X-PAYMENT-RESPONSE"));
|
|
11377
11723
|
const status2 = response.status;
|
|
11724
|
+
const hold = hasPaymentIdentifier(payload) && (!response.ok || replay && peekReceipt(http, getHeader)?.success !== true);
|
|
11378
11725
|
if (status2 === 429 && !receipted) {
|
|
11379
11726
|
return {
|
|
11380
11727
|
kind: "in_doubt",
|
|
@@ -11384,12 +11731,27 @@ var SolanaBatchPayer = class {
|
|
|
11384
11731
|
status: status2,
|
|
11385
11732
|
retryAfterMs: parseRetryAfter(getHeader("retry-after")),
|
|
11386
11733
|
errorReason: await rateLimitReason(response, void 0),
|
|
11387
|
-
detail: replay ? "the replay was answered 429 without a receipt" : "a 429 without a receipt"
|
|
11734
|
+
detail: replay ? "the replay was answered 429 without a receipt" : "a 429 without a receipt",
|
|
11735
|
+
...hold ? { held: response } : {}
|
|
11388
11736
|
};
|
|
11389
11737
|
}
|
|
11738
|
+
const outcome = await this.classifyAnswer(wallet, sent, response, replay, hold);
|
|
11739
|
+
if (!hold) return outcome;
|
|
11740
|
+
if (outcome.kind === "in_doubt") return { ...outcome, settled: false, held: response };
|
|
11741
|
+
await this.settle(http, payload, getHeader, status2);
|
|
11742
|
+
return outcome;
|
|
11743
|
+
}
|
|
11744
|
+
/**
|
|
11745
|
+
* {@link classify}, past a receipt-less 429. With `hold`, the answer is not
|
|
11746
|
+
* handed to the scheme: its receipt is only read.
|
|
11747
|
+
*/
|
|
11748
|
+
async classifyAnswer(wallet, sent, response, replay, hold) {
|
|
11749
|
+
const { http, payload } = sent;
|
|
11750
|
+
const getHeader = (name) => response.headers.get(name);
|
|
11751
|
+
const status2 = response.status;
|
|
11390
11752
|
const channelId = payloadChannelId(payload);
|
|
11391
11753
|
const before = channelId ? confirmedState(wallet.book.find(channelId)?.record) : void 0;
|
|
11392
|
-
const reconciled = await this.settle(http, payload, getHeader, status2);
|
|
11754
|
+
const reconciled = hold ? { settleResponse: peekReceipt(http, getHeader), ok: false } : await this.settle(http, payload, getHeader, status2);
|
|
11393
11755
|
const receipt = reconciled.settleResponse;
|
|
11394
11756
|
const definitive = receipt?.success === true && reconciled.ok && recordAdvanced(before, channelId ? confirmedState(wallet.book.find(channelId)?.record) : void 0, payload, receipt);
|
|
11395
11757
|
if (!response.ok && !replay && sent.kind === "authorization" && cancelledReceipt(receipt)) {
|
|
@@ -11452,7 +11814,15 @@ var SolanaBatchPayer = class {
|
|
|
11452
11814
|
cause: await afterPaymentError(response)
|
|
11453
11815
|
};
|
|
11454
11816
|
}
|
|
11455
|
-
|
|
11817
|
+
const refused = provesNothingBroadcast(receipt);
|
|
11818
|
+
return {
|
|
11819
|
+
kind: "not_charged",
|
|
11820
|
+
reason: "payment_required",
|
|
11821
|
+
status: status2,
|
|
11822
|
+
errorReason: receipt?.errorReason,
|
|
11823
|
+
depositSafe: refused,
|
|
11824
|
+
...refused ? { refusedForMs: refusalCooldownMs(receipt?.errorReason, parseRetryAfter(getHeader("retry-after"))) } : {}
|
|
11825
|
+
};
|
|
11456
11826
|
}
|
|
11457
11827
|
const refusal = await batchRefusal(response);
|
|
11458
11828
|
if (refusal) {
|
|
@@ -11483,7 +11853,7 @@ var SolanaBatchPayer = class {
|
|
|
11483
11853
|
const http = (await this.build(wallet)).http;
|
|
11484
11854
|
try {
|
|
11485
11855
|
await this.prepareTopUp(wallet, paymentRequired);
|
|
11486
|
-
return { http, payload: await http.createPaymentPayload(withoutClientSignedBatch(paymentRequired)) };
|
|
11856
|
+
return { http, payload: await http.createPaymentPayload(withPaymentIdentifier(withoutClientSignedBatch(paymentRequired))) };
|
|
11487
11857
|
} catch (err) {
|
|
11488
11858
|
if (pass > 0 || !isResyncRequired(err) || wallet.resyncs.size === 0) throw err;
|
|
11489
11859
|
} finally {
|
|
@@ -11576,13 +11946,7 @@ var SolanaBatchPayer = class {
|
|
|
11576
11946
|
console.error(
|
|
11577
11947
|
`[@blockrun/llm] batch-settlement receipt not reconciled: ${JSON.stringify(err instanceof Error ? err.message : String(err))}`
|
|
11578
11948
|
);
|
|
11579
|
-
|
|
11580
|
-
try {
|
|
11581
|
-
settleResponse = http.getPaymentSettleResponse(getHeader);
|
|
11582
|
-
} catch {
|
|
11583
|
-
settleResponse = void 0;
|
|
11584
|
-
}
|
|
11585
|
-
return { settleResponse, ok: false };
|
|
11949
|
+
return { settleResponse: peekReceipt(http, getHeader), ok: false };
|
|
11586
11950
|
}
|
|
11587
11951
|
}
|
|
11588
11952
|
/**
|
|
@@ -11965,9 +12329,9 @@ var SolanaLLMClient = class {
|
|
|
11965
12329
|
};
|
|
11966
12330
|
if (options?.temperature !== void 0) body.temperature = options.temperature;
|
|
11967
12331
|
if (options?.topP !== void 0) body.top_p = options.topP;
|
|
11968
|
-
if (options?.searchParameters !== void 0) body.search_parameters = options.searchParameters;
|
|
12332
|
+
if (options?.searchParameters !== void 0) body.search_parameters = toWireSearchParameters(options.searchParameters);
|
|
11969
12333
|
else if (options?.search === true) body.search_parameters = { mode: "on" };
|
|
11970
|
-
if (options?.tools !== void 0) body.tools = options.tools;
|
|
12334
|
+
if (options?.tools !== void 0) body.tools = toWireTools(options.tools);
|
|
11971
12335
|
if (options?.toolChoice !== void 0) body.tool_choice = options.toolChoice;
|
|
11972
12336
|
if (options?.responseFormat !== void 0) body.response_format = options.responseFormat;
|
|
11973
12337
|
if (options?.stop !== void 0) body.stop = options.stop;
|
|
@@ -11978,7 +12342,7 @@ var SolanaLLMClient = class {
|
|
|
11978
12342
|
for (let index = 0; index < chain.length; index += 1) {
|
|
11979
12343
|
const candidate = chain[index];
|
|
11980
12344
|
try {
|
|
11981
|
-
return await this.requestWithPayment("/v1/chat/completions", buildBody(candidate));
|
|
12345
|
+
return await this.requestWithPayment("/v1/chat/completions", buildBody(candidate), options?.timeout);
|
|
11982
12346
|
} catch (error) {
|
|
11983
12347
|
lastError = error;
|
|
11984
12348
|
if (!isTransientError(error) || index === chain.length - 1) throw error;
|
|
@@ -12028,6 +12392,8 @@ var SolanaLLMClient = class {
|
|
|
12028
12392
|
{
|
|
12029
12393
|
routingProfile: options?.routingProfile,
|
|
12030
12394
|
requiresStructuredOutput: options?.responseFormat !== void 0,
|
|
12395
|
+
search: options?.search,
|
|
12396
|
+
searchParameters: options?.searchParameters,
|
|
12031
12397
|
minimumPaymentUsd: this.apiAuth ? 0 : SOLANA_MINIMUM_PAYMENT_USD
|
|
12032
12398
|
}
|
|
12033
12399
|
);
|
|
@@ -12055,6 +12421,8 @@ var SolanaLLMClient = class {
|
|
|
12055
12421
|
requiresStructuredOutput: options.responseFormat !== void 0,
|
|
12056
12422
|
tools: options.tools,
|
|
12057
12423
|
toolChoice: options.toolChoice,
|
|
12424
|
+
search: options.search,
|
|
12425
|
+
searchParameters: options.searchParameters,
|
|
12058
12426
|
conversationChars,
|
|
12059
12427
|
hasVision,
|
|
12060
12428
|
minimumPaymentUsd: this.apiAuth ? 0 : SOLANA_MINIMUM_PAYMENT_USD
|
|
@@ -12070,6 +12438,7 @@ var SolanaLLMClient = class {
|
|
|
12070
12438
|
toolChoice: options.toolChoice,
|
|
12071
12439
|
responseFormat: options.responseFormat,
|
|
12072
12440
|
stop: options.stop,
|
|
12441
|
+
timeout: options.timeout,
|
|
12073
12442
|
// An explicit caller-supplied chain wins over the routed one.
|
|
12074
12443
|
fallbackModels: options.fallbackModels ?? decision.fallbacks
|
|
12075
12444
|
});
|
|
@@ -12207,7 +12576,7 @@ var SolanaLLMClient = class {
|
|
|
12207
12576
|
*
|
|
12208
12577
|
* @returns USDC balance as a float
|
|
12209
12578
|
*/
|
|
12210
|
-
async getBalance() {
|
|
12579
|
+
async getBalance(options) {
|
|
12211
12580
|
const usdc_mint = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
12212
12581
|
const address = await this.getWalletAddress();
|
|
12213
12582
|
try {
|
|
@@ -12222,6 +12591,9 @@ var SolanaLLMClient = class {
|
|
|
12222
12591
|
})
|
|
12223
12592
|
});
|
|
12224
12593
|
const data = await response.json();
|
|
12594
|
+
if (options?.strict && (!response.ok || data.error)) {
|
|
12595
|
+
throw new Error(`Solana RPC balance lookup failed: ${data.error?.message ?? `HTTP ${response.status}`}`);
|
|
12596
|
+
}
|
|
12225
12597
|
const accounts = data.result?.value || [];
|
|
12226
12598
|
if (!accounts.length) return 0;
|
|
12227
12599
|
let total = 0;
|
|
@@ -12229,7 +12601,8 @@ var SolanaLLMClient = class {
|
|
|
12229
12601
|
total += acct.account?.data?.parsed?.info?.tokenAmount?.uiAmount || 0;
|
|
12230
12602
|
}
|
|
12231
12603
|
return total;
|
|
12232
|
-
} catch {
|
|
12604
|
+
} catch (error) {
|
|
12605
|
+
if (options?.strict) throw error;
|
|
12233
12606
|
return 0;
|
|
12234
12607
|
}
|
|
12235
12608
|
}
|
|
@@ -12504,15 +12877,16 @@ var SolanaLLMClient = class {
|
|
|
12504
12877
|
* `"paid-or-in-doubt"`, and so is any error with an API key once the billed
|
|
12505
12878
|
* request was sent, except the account API's explicit 4xx refusal.
|
|
12506
12879
|
*/
|
|
12507
|
-
async requestWithPayment(endpoint, body) {
|
|
12880
|
+
async requestWithPayment(endpoint, body, timeoutMs) {
|
|
12508
12881
|
const url = `${this.apiUrl}${endpoint}`;
|
|
12882
|
+
const json = JSON.stringify(body);
|
|
12509
12883
|
const closesAtStart = await this.batchPayer?.closeFence();
|
|
12510
12884
|
for (let staleRetries = 0; ; ) {
|
|
12511
12885
|
const response = await this.sendUnpaid(url, {
|
|
12512
12886
|
method: "POST",
|
|
12513
12887
|
headers: { "Content-Type": "application/json", "User-Agent": USER_AGENT },
|
|
12514
|
-
body:
|
|
12515
|
-
});
|
|
12888
|
+
body: json
|
|
12889
|
+
}, timeoutMs);
|
|
12516
12890
|
if (response.status === 402) {
|
|
12517
12891
|
try {
|
|
12518
12892
|
let paymentRequired;
|
|
@@ -12528,16 +12902,16 @@ var SolanaLLMClient = class {
|
|
|
12528
12902
|
(paymentHeaders) => this.fetchWithTimeout(url, {
|
|
12529
12903
|
method: "POST",
|
|
12530
12904
|
headers: { "Content-Type": "application/json", "User-Agent": USER_AGENT, ...paymentHeaders },
|
|
12531
|
-
body:
|
|
12532
|
-
}),
|
|
12905
|
+
body: json
|
|
12906
|
+
}, timeoutMs),
|
|
12533
12907
|
// A fresh, unpaid challenge after a 429 wait: the request itself,
|
|
12534
12908
|
// sent again without a payment. Every error it throws is unpaid.
|
|
12535
12909
|
async () => {
|
|
12536
12910
|
const challenge = await this.sendUnpaid(url, {
|
|
12537
12911
|
method: "POST",
|
|
12538
12912
|
headers: { "Content-Type": "application/json", "User-Agent": USER_AGENT },
|
|
12539
|
-
body:
|
|
12540
|
-
});
|
|
12913
|
+
body: json
|
|
12914
|
+
}, timeoutMs);
|
|
12541
12915
|
if (challenge.status === 402) {
|
|
12542
12916
|
try {
|
|
12543
12917
|
return { kind: "challenge", paymentRequired: await this.readPaymentRequired(challenge) };
|
|
@@ -12552,13 +12926,14 @@ var SolanaLLMClient = class {
|
|
|
12552
12926
|
);
|
|
12553
12927
|
if (batch.kind === "paid") {
|
|
12554
12928
|
this.recordSettlement(batch.chargedUsd);
|
|
12555
|
-
|
|
12929
|
+
const paid = await this.readPaidJson(batch.response);
|
|
12930
|
+
return withCost(paid, batch.response, batch.chargedUsd, "batch");
|
|
12556
12931
|
}
|
|
12557
|
-
if (batch.kind === "served") return await this.readUnpaidJson(batch.response);
|
|
12932
|
+
if (batch.kind === "served") return this.withUnpaidCost(await this.readUnpaidJson(batch.response));
|
|
12558
12933
|
if (batch.kind === "failed") throw batch.error;
|
|
12559
12934
|
exactRequired = batch.paymentRequired ?? paymentRequired;
|
|
12560
12935
|
}
|
|
12561
|
-
return await this.handlePaymentAndRetry(url,
|
|
12936
|
+
return await this.handlePaymentAndRetry(url, json, exactRequired, staleRetries > 0, timeoutMs);
|
|
12562
12937
|
} catch (error) {
|
|
12563
12938
|
if (!(error instanceof SafeStaleBlockhashError) || staleRetries >= STALE_BLOCKHASH_RETRY_BACKOFFS_MS.length) throw error;
|
|
12564
12939
|
await waitForStaleRetry(staleRetries++);
|
|
@@ -12566,9 +12941,21 @@ var SolanaLLMClient = class {
|
|
|
12566
12941
|
}
|
|
12567
12942
|
}
|
|
12568
12943
|
if (!response.ok) throw await unpaidApiError(response);
|
|
12569
|
-
return await this.readUnpaidJson(response);
|
|
12944
|
+
return this.withUnpaidCost(await this.readUnpaidJson(response));
|
|
12570
12945
|
}
|
|
12571
12946
|
}
|
|
12947
|
+
/**
|
|
12948
|
+
* Cost of a chat response served without a payment: 0 with a wallet (a free
|
|
12949
|
+
* model). With an API key the call is billed to the account, so costUsd stays
|
|
12950
|
+
* undefined (see the dashboard).
|
|
12951
|
+
*/
|
|
12952
|
+
withUnpaidCost(served) {
|
|
12953
|
+
if (!this.apiAuth) {
|
|
12954
|
+
served.costUsd = 0;
|
|
12955
|
+
served.settlement = { scheme: "free", quotedUsd: 0 };
|
|
12956
|
+
}
|
|
12957
|
+
return served;
|
|
12958
|
+
}
|
|
12572
12959
|
/**
|
|
12573
12960
|
* Read the JSON body of a 2xx served without a payment: the free tier, or
|
|
12574
12961
|
* with an API key, where this very request was the billed one.
|
|
@@ -12585,9 +12972,9 @@ var SolanaLLMClient = class {
|
|
|
12585
12972
|
* {@link RetryDisposition}: `"unpaid"` with a wallet; with an API key, see
|
|
12586
12973
|
* {@link accountErrorDisposition} (the request itself is billed).
|
|
12587
12974
|
*/
|
|
12588
|
-
async sendUnpaid(url, init) {
|
|
12975
|
+
async sendUnpaid(url, init, timeoutMs) {
|
|
12589
12976
|
try {
|
|
12590
|
-
return await this.fetchWithTimeout(url, init);
|
|
12977
|
+
return await this.fetchWithTimeout(url, init, timeoutMs);
|
|
12591
12978
|
} catch (error) {
|
|
12592
12979
|
throw withDisposition(error, this.apiAuth ? accountErrorDisposition(error) : "unpaid");
|
|
12593
12980
|
}
|
|
@@ -12856,7 +13243,7 @@ var SolanaLLMClient = class {
|
|
|
12856
13243
|
if (!feePayer) throw new PaymentError("Missing feePayer in 402 extra field");
|
|
12857
13244
|
const fromAddress = await this.getWalletAddress();
|
|
12858
13245
|
const secretKey = await solanaKeyToBytes(this.privateKey);
|
|
12859
|
-
const extensions = paymentRequired.extensions;
|
|
13246
|
+
const extensions = withPaymentIdentifier(paymentRequired).extensions;
|
|
12860
13247
|
const paymentPayload = await createSolanaPaymentPayload(
|
|
12861
13248
|
secretKey,
|
|
12862
13249
|
fromAddress,
|
|
@@ -12879,7 +13266,7 @@ var SolanaLLMClient = class {
|
|
|
12879
13266
|
);
|
|
12880
13267
|
return { paymentPayload, costUsd: parseFloat(details.amount) / 1e6 };
|
|
12881
13268
|
}
|
|
12882
|
-
async handlePaymentAndRetry(url,
|
|
13269
|
+
async handlePaymentAndRetry(url, json, paymentRequired, forceFreshBlockhash = false, timeoutMs) {
|
|
12883
13270
|
let signed;
|
|
12884
13271
|
try {
|
|
12885
13272
|
signed = await this.signExactPayment(url, paymentRequired, forceFreshBlockhash, `${this.apiUrl}/v1/chat/completions`);
|
|
@@ -12896,14 +13283,15 @@ var SolanaLLMClient = class {
|
|
|
12896
13283
|
"User-Agent": USER_AGENT,
|
|
12897
13284
|
"PAYMENT-SIGNATURE": paymentPayload
|
|
12898
13285
|
},
|
|
12899
|
-
body:
|
|
12900
|
-
});
|
|
13286
|
+
body: json
|
|
13287
|
+
}, timeoutMs);
|
|
12901
13288
|
} catch (error) {
|
|
12902
13289
|
throw withDisposition(error, "paid-or-in-doubt");
|
|
12903
13290
|
}
|
|
12904
13291
|
await this.assertPaid(retryResponse);
|
|
12905
|
-
this.recordSettlement(costUsd);
|
|
12906
|
-
|
|
13292
|
+
if (!settlementSkipped(retryResponse)) this.recordSettlement(costUsd);
|
|
13293
|
+
const paid = await this.readPaidJson(retryResponse);
|
|
13294
|
+
return withCost(paid, retryResponse, costUsd, "exact");
|
|
12907
13295
|
}
|
|
12908
13296
|
async requestWithPaymentRaw(endpoint, body, media = {}) {
|
|
12909
13297
|
const url = `${this.apiUrl}${endpoint}`;
|
|
@@ -13618,6 +14006,7 @@ export {
|
|
|
13618
14006
|
BatchPaymentUnresolvedError,
|
|
13619
14007
|
BlockrunClient,
|
|
13620
14008
|
BlockrunError,
|
|
14009
|
+
DEFAULT_BATCH_IN_DOUBT,
|
|
13621
14010
|
DEFAULT_BATCH_RATE_LIMIT,
|
|
13622
14011
|
EVM_NETWORKS,
|
|
13623
14012
|
ImageClient,
|
|
@@ -13681,6 +14070,7 @@ export {
|
|
|
13681
14070
|
loadWallet,
|
|
13682
14071
|
logCost,
|
|
13683
14072
|
parsePaymentRequired,
|
|
14073
|
+
readSettlement,
|
|
13684
14074
|
retryDisposition,
|
|
13685
14075
|
saveSolanaWallet,
|
|
13686
14076
|
saveToCache,
|
|
@@ -13695,6 +14085,8 @@ export {
|
|
|
13695
14085
|
solanaKeyToBytes,
|
|
13696
14086
|
solanaPublicKey,
|
|
13697
14087
|
status,
|
|
14088
|
+
toWireSearchParameters,
|
|
14089
|
+
toWireTools,
|
|
13698
14090
|
validateMaxTokens,
|
|
13699
14091
|
validateModel,
|
|
13700
14092
|
validateTemperature,
|