@blockrun/llm 3.21.0 → 3.22.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 +67 -12
- package/dist/index.cjs +472 -91
- package/dist/index.d.cts +201 -17
- package/dist/index.d.ts +201 -17
- package/dist/index.js +468 -91
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -40,6 +40,7 @@ __export(index_exports, {
|
|
|
40
40
|
BatchPaymentUnresolvedError: () => BatchPaymentUnresolvedError,
|
|
41
41
|
BlockrunClient: () => BlockrunClient,
|
|
42
42
|
BlockrunError: () => BlockrunError,
|
|
43
|
+
DEFAULT_BATCH_IN_DOUBT: () => DEFAULT_BATCH_IN_DOUBT,
|
|
43
44
|
DEFAULT_BATCH_RATE_LIMIT: () => DEFAULT_BATCH_RATE_LIMIT,
|
|
44
45
|
EVM_NETWORKS: () => EVM_NETWORKS,
|
|
45
46
|
ImageClient: () => ImageClient,
|
|
@@ -103,6 +104,7 @@ __export(index_exports, {
|
|
|
103
104
|
loadWallet: () => loadWallet,
|
|
104
105
|
logCost: () => logCost,
|
|
105
106
|
parsePaymentRequired: () => parsePaymentRequired,
|
|
107
|
+
readSettlement: () => readSettlement,
|
|
106
108
|
retryDisposition: () => retryDisposition,
|
|
107
109
|
saveSolanaWallet: () => saveSolanaWallet,
|
|
108
110
|
saveToCache: () => saveToCache,
|
|
@@ -117,6 +119,8 @@ __export(index_exports, {
|
|
|
117
119
|
solanaKeyToBytes: () => solanaKeyToBytes,
|
|
118
120
|
solanaPublicKey: () => solanaPublicKey,
|
|
119
121
|
status: () => status,
|
|
122
|
+
toWireSearchParameters: () => toWireSearchParameters,
|
|
123
|
+
toWireTools: () => toWireTools,
|
|
120
124
|
validateMaxTokens: () => validateMaxTokens,
|
|
121
125
|
validateModel: () => validateModel,
|
|
122
126
|
validateTemperature: () => validateTemperature,
|
|
@@ -439,6 +443,122 @@ var ApiKeyAuth = class {
|
|
|
439
443
|
}
|
|
440
444
|
};
|
|
441
445
|
|
|
446
|
+
// src/search-wire.ts
|
|
447
|
+
function put(out, key, value) {
|
|
448
|
+
if (value !== void 0) out[key] = value;
|
|
449
|
+
}
|
|
450
|
+
function handles(list) {
|
|
451
|
+
return list?.map((h) => h.trim().replace(/^@+/, ""));
|
|
452
|
+
}
|
|
453
|
+
function sourceToWire(source) {
|
|
454
|
+
const raw = source;
|
|
455
|
+
const out = { ...raw };
|
|
456
|
+
for (const key of Object.keys(out)) if (/[A-Z]/.test(key)) delete out[key];
|
|
457
|
+
switch (source.type) {
|
|
458
|
+
case "x":
|
|
459
|
+
put(out, "included_x_handles", handles(source.includedXHandles) ?? out.included_x_handles);
|
|
460
|
+
put(out, "excluded_x_handles", handles(source.excludedXHandles) ?? out.excluded_x_handles);
|
|
461
|
+
put(out, "post_favorite_count", source.postFavoriteCount);
|
|
462
|
+
put(out, "post_view_count", source.postViewCount);
|
|
463
|
+
put(out, "enable_image_understanding", source.enableImageUnderstanding);
|
|
464
|
+
put(out, "enable_video_understanding", source.enableVideoUnderstanding);
|
|
465
|
+
break;
|
|
466
|
+
case "web":
|
|
467
|
+
put(out, "country", source.country);
|
|
468
|
+
put(out, "allowed_websites", source.allowedWebsites);
|
|
469
|
+
put(out, "excluded_websites", source.excludedWebsites);
|
|
470
|
+
put(out, "safe_search", source.safeSearch);
|
|
471
|
+
put(out, "enable_image_understanding", source.enableImageUnderstanding);
|
|
472
|
+
break;
|
|
473
|
+
case "news":
|
|
474
|
+
put(out, "country", source.country);
|
|
475
|
+
put(out, "allowed_websites", source.allowedWebsites);
|
|
476
|
+
put(out, "excluded_websites", source.excludedWebsites);
|
|
477
|
+
put(out, "safe_search", source.safeSearch);
|
|
478
|
+
break;
|
|
479
|
+
case "rss":
|
|
480
|
+
put(out, "links", source.links);
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
return out;
|
|
484
|
+
}
|
|
485
|
+
function toWireSearchParameters(sp) {
|
|
486
|
+
const raw = sp;
|
|
487
|
+
const out = {};
|
|
488
|
+
for (const [key, value] of Object.entries(raw)) if (!/[A-Z]/.test(key) && key !== "sources") out[key] = value;
|
|
489
|
+
put(out, "mode", sp.mode);
|
|
490
|
+
if (sp.sources) out.sources = sp.sources.map(sourceToWire);
|
|
491
|
+
put(out, "return_citations", sp.returnCitations);
|
|
492
|
+
put(out, "from_date", sp.fromDate);
|
|
493
|
+
put(out, "to_date", sp.toDate);
|
|
494
|
+
put(out, "max_search_results", sp.maxSearchResults);
|
|
495
|
+
put(out, "max_turns", sp.maxTurns);
|
|
496
|
+
return out;
|
|
497
|
+
}
|
|
498
|
+
function toWireTools(tools) {
|
|
499
|
+
return tools.map((tool) => {
|
|
500
|
+
if (tool.type === "x_search") {
|
|
501
|
+
const out = { type: "x_search" };
|
|
502
|
+
put(out, "allowed_x_handles", handles(tool.allowedXHandles));
|
|
503
|
+
put(out, "excluded_x_handles", handles(tool.excludedXHandles));
|
|
504
|
+
put(out, "from_date", tool.fromDate);
|
|
505
|
+
put(out, "to_date", tool.toDate);
|
|
506
|
+
put(out, "enable_image_understanding", tool.enableImageUnderstanding);
|
|
507
|
+
put(out, "enable_video_understanding", tool.enableVideoUnderstanding);
|
|
508
|
+
return out;
|
|
509
|
+
}
|
|
510
|
+
if (tool.type === "web_search") {
|
|
511
|
+
const out = { type: "web_search" };
|
|
512
|
+
put(out, "allowed_domains", tool.allowedDomains);
|
|
513
|
+
put(out, "excluded_domains", tool.excludedDomains);
|
|
514
|
+
put(out, "enable_image_understanding", tool.enableImageUnderstanding);
|
|
515
|
+
return out;
|
|
516
|
+
}
|
|
517
|
+
return tool;
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// src/receipt.ts
|
|
522
|
+
function atomicToUsd(amount) {
|
|
523
|
+
return typeof amount === "string" && /^\d+$/.test(amount) ? Number(amount) / 1e6 : void 0;
|
|
524
|
+
}
|
|
525
|
+
function receiptChargedUsd(receipt) {
|
|
526
|
+
return atomicToUsd(receipt?.extra?.chargedAmount) ?? atomicToUsd(receipt?.amount);
|
|
527
|
+
}
|
|
528
|
+
function decode(header) {
|
|
529
|
+
try {
|
|
530
|
+
const json = typeof Buffer !== "undefined" ? Buffer.from(header, "base64").toString("utf8") : atob(header);
|
|
531
|
+
const parsed = JSON.parse(json);
|
|
532
|
+
return parsed && typeof parsed === "object" ? parsed : void 0;
|
|
533
|
+
} catch {
|
|
534
|
+
return void 0;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
function readSettlement(response, quotedUsd, scheme) {
|
|
538
|
+
const header = response.headers.get("PAYMENT-RESPONSE") ?? response.headers.get("X-PAYMENT-RESPONSE");
|
|
539
|
+
const receipt = header ? decode(header) : void 0;
|
|
540
|
+
const extra = receipt?.extra;
|
|
541
|
+
const costUsd = receiptChargedUsd({
|
|
542
|
+
amount: receipt?.amount,
|
|
543
|
+
extra: extra && typeof extra === "object" ? extra : void 0
|
|
544
|
+
}) ?? quotedUsd;
|
|
545
|
+
const receiptTx = typeof receipt?.transaction === "string" && receipt.transaction ? receipt.transaction : void 0;
|
|
546
|
+
const transaction = receiptTx ?? (response.headers.get("X-Payment-Receipt")?.trim() || void 0);
|
|
547
|
+
const settlement = {
|
|
548
|
+
scheme,
|
|
549
|
+
quotedUsd,
|
|
550
|
+
...transaction ? { transaction } : {},
|
|
551
|
+
...typeof receipt?.network === "string" ? { network: receipt.network } : {},
|
|
552
|
+
...receipt?.success === false ? { success: false } : {}
|
|
553
|
+
};
|
|
554
|
+
return { costUsd, settlement };
|
|
555
|
+
}
|
|
556
|
+
function withCost(body, response, bookedUsd, scheme) {
|
|
557
|
+
body.costUsd = bookedUsd;
|
|
558
|
+
body.settlement = readSettlement(response, bookedUsd, scheme).settlement;
|
|
559
|
+
return body;
|
|
560
|
+
}
|
|
561
|
+
|
|
442
562
|
// src/client.ts
|
|
443
563
|
var import_accounts2 = require("viem/accounts");
|
|
444
564
|
|
|
@@ -4225,6 +4345,11 @@ function errSummary(err) {
|
|
|
4225
4345
|
}
|
|
4226
4346
|
return String(err).slice(0, 100);
|
|
4227
4347
|
}
|
|
4348
|
+
function requestsSearch(options) {
|
|
4349
|
+
if (options.tools?.some((tool) => tool.type === "x_search" || tool.type === "web_search")) return true;
|
|
4350
|
+
if (options.searchParameters !== void 0) return options.searchParameters.mode !== "off";
|
|
4351
|
+
return options.search === true;
|
|
4352
|
+
}
|
|
4228
4353
|
function routingProfileForModel(model) {
|
|
4229
4354
|
return AUTO_ROUTING_PROFILES[model.toLowerCase()];
|
|
4230
4355
|
}
|
|
@@ -4252,7 +4377,7 @@ function routingText(messages) {
|
|
|
4252
4377
|
};
|
|
4253
4378
|
}
|
|
4254
4379
|
function routeWithCatalog(prompt, systemPrompt, maxOutputTokens, modelPricing, options = {}) {
|
|
4255
|
-
const tools = options.tools ?? [];
|
|
4380
|
+
const tools = (options.tools ?? []).filter((tool) => tool.type === "function");
|
|
4256
4381
|
const requiresTools = options.toolChoice === "none" ? false : options.toolChoice === "required" || typeof options.toolChoice === "object" ? true : void 0;
|
|
4257
4382
|
const decision = route(prompt, systemPrompt, maxOutputTokens, {
|
|
4258
4383
|
config: DEFAULT_ROUTING_CONFIG,
|
|
@@ -4272,6 +4397,15 @@ function routeWithCatalog(prompt, systemPrompt, maxOutputTokens, modelPricing, o
|
|
|
4272
4397
|
const resolved = !id.startsWith("free/") ? id : modelPricing.has(`nvidia/${id.slice(5)}`) ? `nvidia/${id.slice(5)}` : null;
|
|
4273
4398
|
if (resolved && !callable.includes(resolved)) callable.push(resolved);
|
|
4274
4399
|
}
|
|
4400
|
+
if (requestsSearch(options)) {
|
|
4401
|
+
const searchable = callable.filter((id) => id.startsWith("xai/"));
|
|
4402
|
+
if (searchable.length === 0) {
|
|
4403
|
+
throw new Error(
|
|
4404
|
+
`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.`
|
|
4405
|
+
);
|
|
4406
|
+
}
|
|
4407
|
+
callable.splice(0, callable.length, ...searchable);
|
|
4408
|
+
}
|
|
4275
4409
|
const estimatedInputTokens = Math.ceil(
|
|
4276
4410
|
Math.max(options.conversationChars ?? 0, `${systemPrompt ?? ""} ${prompt}`.length) / 4
|
|
4277
4411
|
);
|
|
@@ -4690,7 +4824,7 @@ function getCostSummary() {
|
|
|
4690
4824
|
}
|
|
4691
4825
|
|
|
4692
4826
|
// src/version.ts
|
|
4693
|
-
var SDK_VERSION = "3.
|
|
4827
|
+
var SDK_VERSION = "3.22.0";
|
|
4694
4828
|
var USER_AGENT = `blockrun-ts/${SDK_VERSION}`;
|
|
4695
4829
|
|
|
4696
4830
|
// src/client.ts
|
|
@@ -4836,7 +4970,9 @@ var LLMClient = class _LLMClient {
|
|
|
4836
4970
|
options?.maxOutputTokens ?? options?.maxTokens ?? DEFAULT_MAX_TOKENS,
|
|
4837
4971
|
{
|
|
4838
4972
|
routingProfile: options?.routingProfile,
|
|
4839
|
-
requiresStructuredOutput: options?.responseFormat !== void 0
|
|
4973
|
+
requiresStructuredOutput: options?.responseFormat !== void 0,
|
|
4974
|
+
search: options?.search,
|
|
4975
|
+
searchParameters: options?.searchParameters
|
|
4840
4976
|
}
|
|
4841
4977
|
);
|
|
4842
4978
|
}
|
|
@@ -4903,6 +5039,8 @@ var LLMClient = class _LLMClient {
|
|
|
4903
5039
|
requiresStructuredOutput: options.responseFormat !== void 0,
|
|
4904
5040
|
tools: options.tools,
|
|
4905
5041
|
toolChoice: options.toolChoice,
|
|
5042
|
+
search: options.search,
|
|
5043
|
+
searchParameters: options.searchParameters,
|
|
4906
5044
|
conversationChars,
|
|
4907
5045
|
hasVision
|
|
4908
5046
|
}
|
|
@@ -4917,6 +5055,7 @@ var LLMClient = class _LLMClient {
|
|
|
4917
5055
|
toolChoice: options.toolChoice,
|
|
4918
5056
|
responseFormat: options.responseFormat,
|
|
4919
5057
|
stop: options.stop,
|
|
5058
|
+
timeout: options.timeout,
|
|
4920
5059
|
// An explicit caller-supplied chain wins over the routed one.
|
|
4921
5060
|
fallbackModels: options.fallbackModels ?? decision.fallbacks
|
|
4922
5061
|
});
|
|
@@ -4993,11 +5132,11 @@ var LLMClient = class _LLMClient {
|
|
|
4993
5132
|
if (options?.temperature !== void 0) body.temperature = options.temperature;
|
|
4994
5133
|
if (options?.topP !== void 0) body.top_p = options.topP;
|
|
4995
5134
|
if (options?.searchParameters !== void 0) {
|
|
4996
|
-
body.search_parameters = options.searchParameters;
|
|
5135
|
+
body.search_parameters = toWireSearchParameters(options.searchParameters);
|
|
4997
5136
|
} else if (options?.search === true) {
|
|
4998
5137
|
body.search_parameters = { mode: "on" };
|
|
4999
5138
|
}
|
|
5000
|
-
if (options?.tools !== void 0) body.tools = options.tools;
|
|
5139
|
+
if (options?.tools !== void 0) body.tools = toWireTools(options.tools);
|
|
5001
5140
|
if (options?.toolChoice !== void 0) body.tool_choice = options.toolChoice;
|
|
5002
5141
|
if (options?.responseFormat !== void 0) body.response_format = options.responseFormat;
|
|
5003
5142
|
if (options?.stop !== void 0) body.stop = options.stop;
|
|
@@ -5008,7 +5147,7 @@ var LLMClient = class _LLMClient {
|
|
|
5008
5147
|
for (let i = 0; i < chain.length; i++) {
|
|
5009
5148
|
const candidate = chain[i];
|
|
5010
5149
|
try {
|
|
5011
|
-
return await this.requestWithPayment("/v1/chat/completions", buildBody(candidate));
|
|
5150
|
+
return await this.requestWithPayment("/v1/chat/completions", buildBody(candidate), options?.timeout);
|
|
5012
5151
|
} catch (err) {
|
|
5013
5152
|
lastErr = err;
|
|
5014
5153
|
const next = chain[i + 1];
|
|
@@ -5079,19 +5218,19 @@ var LLMClient = class _LLMClient {
|
|
|
5079
5218
|
* is `"paid-or-in-doubt"`. With an API key the request itself is billed, so
|
|
5080
5219
|
* only the account API's explicit 4xx refusal is `"unpaid"`.
|
|
5081
5220
|
*/
|
|
5082
|
-
async requestWithPayment(endpoint, body) {
|
|
5221
|
+
async requestWithPayment(endpoint, body, timeoutMs) {
|
|
5083
5222
|
const url = `${this.apiUrl}${endpoint}`;
|
|
5084
5223
|
const unpaid = {
|
|
5085
5224
|
method: "POST",
|
|
5086
5225
|
headers: { "Content-Type": "application/json", "User-Agent": USER_AGENT },
|
|
5087
5226
|
body: JSON.stringify(body)
|
|
5088
5227
|
};
|
|
5089
|
-
const response = await this.sendUnpaid(url, unpaid);
|
|
5228
|
+
const response = await this.sendUnpaid(url, unpaid, timeoutMs);
|
|
5090
5229
|
if (response.status === 502 || response.status === 503) {
|
|
5091
5230
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
5092
|
-
const retryResp = await this.sendUnpaid(url, unpaid);
|
|
5231
|
+
const retryResp = await this.sendUnpaid(url, unpaid, timeoutMs);
|
|
5093
5232
|
if (retryResp.status !== 502 && retryResp.status !== 503) {
|
|
5094
|
-
if (retryResp.status === 402) return this.handlePaymentAndRetry(url, body, retryResp);
|
|
5233
|
+
if (retryResp.status === 402) return this.handlePaymentAndRetry(url, body, retryResp, timeoutMs);
|
|
5095
5234
|
if (!retryResp.ok) {
|
|
5096
5235
|
let errorBody;
|
|
5097
5236
|
try {
|
|
@@ -5108,7 +5247,7 @@ var LLMClient = class _LLMClient {
|
|
|
5108
5247
|
}
|
|
5109
5248
|
}
|
|
5110
5249
|
if (response.status === 402) {
|
|
5111
|
-
return this.handlePaymentAndRetry(url, body, response);
|
|
5250
|
+
return this.handlePaymentAndRetry(url, body, response, timeoutMs);
|
|
5112
5251
|
}
|
|
5113
5252
|
if (!response.ok) {
|
|
5114
5253
|
let errorBody;
|
|
@@ -5133,9 +5272,9 @@ var LLMClient = class _LLMClient {
|
|
|
5133
5272
|
* {@link RetryDisposition}: `"unpaid"` with a wallet; with an API key, see
|
|
5134
5273
|
* {@link accountErrorDisposition} (the request itself is billed).
|
|
5135
5274
|
*/
|
|
5136
|
-
async sendUnpaid(url, init) {
|
|
5275
|
+
async sendUnpaid(url, init, timeoutMs) {
|
|
5137
5276
|
try {
|
|
5138
|
-
return await this.fetchWithTimeout(url, init);
|
|
5277
|
+
return await this.fetchWithTimeout(url, init, timeoutMs);
|
|
5139
5278
|
} catch (err) {
|
|
5140
5279
|
throw withDisposition(err, this.apiAuth ? accountErrorDisposition(err) : "unpaid");
|
|
5141
5280
|
}
|
|
@@ -5143,13 +5282,22 @@ var LLMClient = class _LLMClient {
|
|
|
5143
5282
|
/**
|
|
5144
5283
|
* Parse a response to a request that carried no payment: the free tier,
|
|
5145
5284
|
* or, with an API key, the billed request itself.
|
|
5285
|
+
*
|
|
5286
|
+
* With a wallet nothing was paid, so `costUsd` is 0 (a free model). With an
|
|
5287
|
+
* API key the call is billed to the account and `costUsd` stays undefined.
|
|
5146
5288
|
*/
|
|
5147
5289
|
async parseUnpaidChatResponse(response) {
|
|
5290
|
+
let served;
|
|
5148
5291
|
try {
|
|
5149
|
-
|
|
5292
|
+
served = await this.parseChatResponse(response);
|
|
5150
5293
|
} catch (err) {
|
|
5151
5294
|
throw withDisposition(err, this.apiAuth ? "paid-or-in-doubt" : "unpaid");
|
|
5152
5295
|
}
|
|
5296
|
+
if (!this.apiAuth) {
|
|
5297
|
+
served.costUsd = 0;
|
|
5298
|
+
served.settlement = { scheme: "free", quotedUsd: 0 };
|
|
5299
|
+
}
|
|
5300
|
+
return served;
|
|
5153
5301
|
}
|
|
5154
5302
|
/**
|
|
5155
5303
|
* Handle 402 response: parse requirements, sign payment, retry.
|
|
@@ -5157,7 +5305,7 @@ var LLMClient = class _LLMClient {
|
|
|
5157
5305
|
* Anything thrown before the signed payment is sent is `"unpaid"`; anything
|
|
5158
5306
|
* after it, `"paid-or-in-doubt"` (see {@link RetryDisposition}).
|
|
5159
5307
|
*/
|
|
5160
|
-
async handlePaymentAndRetry(url, body, response) {
|
|
5308
|
+
async handlePaymentAndRetry(url, body, response, timeoutMs) {
|
|
5161
5309
|
let signed;
|
|
5162
5310
|
try {
|
|
5163
5311
|
signed = await this.signFrom402(url, response);
|
|
@@ -5165,7 +5313,7 @@ var LLMClient = class _LLMClient {
|
|
|
5165
5313
|
throw withDisposition(err, "unpaid");
|
|
5166
5314
|
}
|
|
5167
5315
|
try {
|
|
5168
|
-
return await this.sendPaid(url, body, signed.paymentPayload, signed.details);
|
|
5316
|
+
return await this.sendPaid(url, body, signed.paymentPayload, signed.details, timeoutMs);
|
|
5169
5317
|
} catch (err) {
|
|
5170
5318
|
throw withDisposition(err, "paid-or-in-doubt");
|
|
5171
5319
|
}
|
|
@@ -5210,7 +5358,7 @@ var LLMClient = class _LLMClient {
|
|
|
5210
5358
|
return { paymentPayload, details };
|
|
5211
5359
|
}
|
|
5212
5360
|
/** Send the signed payment and read the answer. The payment may settle from here on. */
|
|
5213
|
-
async sendPaid(url, body, paymentPayload, details) {
|
|
5361
|
+
async sendPaid(url, body, paymentPayload, details, timeoutMs) {
|
|
5214
5362
|
const retryResponse = await this.fetchWithTimeout(url, {
|
|
5215
5363
|
method: "POST",
|
|
5216
5364
|
headers: {
|
|
@@ -5219,7 +5367,7 @@ var LLMClient = class _LLMClient {
|
|
|
5219
5367
|
"PAYMENT-SIGNATURE": paymentPayload
|
|
5220
5368
|
},
|
|
5221
5369
|
body: JSON.stringify(body)
|
|
5222
|
-
});
|
|
5370
|
+
}, timeoutMs);
|
|
5223
5371
|
if (retryResponse.status === 502 || retryResponse.status === 503) {
|
|
5224
5372
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
5225
5373
|
const retryResp2 = await this.fetchWithTimeout(url, {
|
|
@@ -5230,7 +5378,7 @@ var LLMClient = class _LLMClient {
|
|
|
5230
5378
|
"PAYMENT-SIGNATURE": paymentPayload
|
|
5231
5379
|
},
|
|
5232
5380
|
body: JSON.stringify(body)
|
|
5233
|
-
});
|
|
5381
|
+
}, timeoutMs);
|
|
5234
5382
|
if (retryResp2.status !== 502 && retryResp2.status !== 503) {
|
|
5235
5383
|
if (retryResp2.status === 402) throw new PaymentError("Payment was rejected. Check your wallet balance.");
|
|
5236
5384
|
if (!retryResp2.ok) {
|
|
@@ -5246,7 +5394,7 @@ var LLMClient = class _LLMClient {
|
|
|
5246
5394
|
this.sessionCalls += 1;
|
|
5247
5395
|
this.sessionTotalUsd += costUsd2;
|
|
5248
5396
|
this.recordCost(url, costUsd2, { body, network: details.network });
|
|
5249
|
-
return this.parseChatResponse(retryResp2);
|
|
5397
|
+
return withCost(await this.parseChatResponse(retryResp2), retryResp2, costUsd2, "exact");
|
|
5250
5398
|
}
|
|
5251
5399
|
}
|
|
5252
5400
|
if (retryResponse.status === 402) {
|
|
@@ -5269,7 +5417,7 @@ var LLMClient = class _LLMClient {
|
|
|
5269
5417
|
this.sessionCalls += 1;
|
|
5270
5418
|
this.sessionTotalUsd += costUsd;
|
|
5271
5419
|
this.recordCost(url, costUsd, { body, network: details.network });
|
|
5272
|
-
return this.parseChatResponse(retryResponse);
|
|
5420
|
+
return withCost(await this.parseChatResponse(retryResponse), retryResponse, costUsd, "exact");
|
|
5273
5421
|
}
|
|
5274
5422
|
/**
|
|
5275
5423
|
* Sign a payment header and return the PAYMENT-SIGNATURE value.
|
|
@@ -5339,7 +5487,7 @@ var LLMClient = class _LLMClient {
|
|
|
5339
5487
|
};
|
|
5340
5488
|
if (options?.temperature !== void 0) body.temperature = options.temperature;
|
|
5341
5489
|
if (options?.topP !== void 0) body.top_p = options.topP;
|
|
5342
|
-
if (options?.tools !== void 0) body.tools = options.tools;
|
|
5490
|
+
if (options?.tools !== void 0) body.tools = toWireTools(options.tools);
|
|
5343
5491
|
if (options?.toolChoice !== void 0) body.tool_choice = options.toolChoice;
|
|
5344
5492
|
if (options?.responseFormat !== void 0) body.response_format = options.responseFormat;
|
|
5345
5493
|
if (options?.stop !== void 0) body.stop = options.stop;
|
|
@@ -5737,11 +5885,11 @@ var LLMClient = class _LLMClient {
|
|
|
5737
5885
|
return retryResponse.json();
|
|
5738
5886
|
}
|
|
5739
5887
|
/**
|
|
5740
|
-
* Fetch with timeout
|
|
5888
|
+
* Fetch with timeout: `timeoutMs` (a per-call override), else the client's `timeout`.
|
|
5741
5889
|
*/
|
|
5742
|
-
async fetchWithTimeout(url, options) {
|
|
5890
|
+
async fetchWithTimeout(url, options, timeoutMs = this.timeout) {
|
|
5743
5891
|
const controller = new AbortController();
|
|
5744
|
-
const timeoutId = setTimeout(() => controller.abort(),
|
|
5892
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
5745
5893
|
try {
|
|
5746
5894
|
const response = await (this.apiAuth ? this.apiAuth.fetch.bind(this.apiAuth) : fetch)(url, {
|
|
5747
5895
|
...options,
|
|
@@ -9726,6 +9874,7 @@ var import_bs582 = __toESM(require("bs58"), 1);
|
|
|
9726
9874
|
var BATCH_SCHEME = "batch-settlement";
|
|
9727
9875
|
var BLOCKRUN_SOL_OPERATOR = "5YKPQUFjw5WQqhSUkEGKNNfYYVqnRRNbpYyL71qQ1vm3";
|
|
9728
9876
|
var DEFAULT_BATCH_RATE_LIMIT = { maxAttempts: 3, maxWaitMs: 6e4 };
|
|
9877
|
+
var DEFAULT_BATCH_IN_DOUBT = { maxWaitMs: 3e5 };
|
|
9729
9878
|
var BatchPaymentUnresolvedError = class extends PaymentError {
|
|
9730
9879
|
/** The {@link SolanaBatchEvent.reason} of the `unresolved` event reported with it. */
|
|
9731
9880
|
reason;
|
|
@@ -10402,6 +10551,46 @@ function withoutClientSignedBatch(paymentRequired) {
|
|
|
10402
10551
|
const accepts = paymentRequired.accepts ?? [];
|
|
10403
10552
|
return accepts.some(clientSignedBatch) ? { ...paymentRequired, accepts: accepts.filter((accept) => !clientSignedBatch(accept)) } : paymentRequired;
|
|
10404
10553
|
}
|
|
10554
|
+
var PAYMENT_IDENTIFIER = "payment-identifier";
|
|
10555
|
+
function withPaymentIdentifier(paymentRequired) {
|
|
10556
|
+
const declared = paymentRequired.extensions?.[PAYMENT_IDENTIFIER];
|
|
10557
|
+
if (typeof declared?.info?.required !== "boolean") return paymentRequired;
|
|
10558
|
+
const id = `pay_${(0, import_crypto2.randomUUID)().replace(/-/g, "")}`;
|
|
10559
|
+
return {
|
|
10560
|
+
...paymentRequired,
|
|
10561
|
+
extensions: { ...paymentRequired.extensions, [PAYMENT_IDENTIFIER]: { ...declared, info: { ...declared.info, id } } }
|
|
10562
|
+
};
|
|
10563
|
+
}
|
|
10564
|
+
function peekReceipt(http, getHeader) {
|
|
10565
|
+
try {
|
|
10566
|
+
return http.getPaymentSettleResponse(getHeader);
|
|
10567
|
+
} catch {
|
|
10568
|
+
return void 0;
|
|
10569
|
+
}
|
|
10570
|
+
}
|
|
10571
|
+
function receiptSucceeded(http, answer) {
|
|
10572
|
+
return peekReceipt(http, (name) => answer.headers.get(name))?.success === true;
|
|
10573
|
+
}
|
|
10574
|
+
function hasPaymentIdentifier(payload) {
|
|
10575
|
+
const info = payload.extensions?.[PAYMENT_IDENTIFIER]?.info;
|
|
10576
|
+
return typeof info?.id === "string";
|
|
10577
|
+
}
|
|
10578
|
+
var IN_DOUBT_MIN_WAIT_MS = 1e3;
|
|
10579
|
+
var IN_DOUBT_MAX_WAIT_MS = 3e4;
|
|
10580
|
+
function replayMayResolve(doubt) {
|
|
10581
|
+
if (doubt.unanswered) return true;
|
|
10582
|
+
const answer = doubt.held;
|
|
10583
|
+
if (!answer) return false;
|
|
10584
|
+
return answer.status >= 500 || answer.status === 429 || answer.status === 409 && Boolean(answer.headers.get("retry-after")?.trim());
|
|
10585
|
+
}
|
|
10586
|
+
function inDoubtWait(doubt, replay) {
|
|
10587
|
+
const asked = doubt.held ? parseRetryAfter(doubt.held.headers.get("retry-after")) : void 0;
|
|
10588
|
+
return Math.min(IN_DOUBT_MAX_WAIT_MS, Math.max(IN_DOUBT_MIN_WAIT_MS, asked ?? backoffDelay(replay)));
|
|
10589
|
+
}
|
|
10590
|
+
function replayDoubt(outcome) {
|
|
10591
|
+
if (outcome.kind === "charged" || outcome.kind === "in_doubt") return outcome;
|
|
10592
|
+
return { kind: "in_doubt", reason: "replay_unresolved", replayable: false, settled: true, detail: "the replay was refused" };
|
|
10593
|
+
}
|
|
10405
10594
|
function trustedBatchAccepts(paymentRequired, operators) {
|
|
10406
10595
|
return (paymentRequired.accepts ?? []).filter((candidate) => {
|
|
10407
10596
|
const extra = candidate.extra;
|
|
@@ -10411,9 +10600,6 @@ function trustedBatchAccepts(paymentRequired, operators) {
|
|
|
10411
10600
|
function trustedBatchAccept(paymentRequired, operators) {
|
|
10412
10601
|
return trustedBatchAccepts(paymentRequired, operators)[0];
|
|
10413
10602
|
}
|
|
10414
|
-
function atomicToUsd(amount) {
|
|
10415
|
-
return typeof amount === "string" && /^\d+$/.test(amount) ? Number(amount) / 1e6 : void 0;
|
|
10416
|
-
}
|
|
10417
10603
|
function chargedUsd(getReceipt) {
|
|
10418
10604
|
let receipt;
|
|
10419
10605
|
try {
|
|
@@ -10421,7 +10607,7 @@ function chargedUsd(getReceipt) {
|
|
|
10421
10607
|
} catch {
|
|
10422
10608
|
return 0;
|
|
10423
10609
|
}
|
|
10424
|
-
return
|
|
10610
|
+
return receiptChargedUsd(receipt) ?? 0;
|
|
10425
10611
|
}
|
|
10426
10612
|
function rebuiltCharge(settled, payload) {
|
|
10427
10613
|
if (settled?.success !== true) return 0n;
|
|
@@ -10541,13 +10727,42 @@ function parseRetryAfter(value, now = Date.now()) {
|
|
|
10541
10727
|
}
|
|
10542
10728
|
return Number.isFinite(ms) && ms <= MAX_RETRY_AFTER_MS ? ms : void 0;
|
|
10543
10729
|
}
|
|
10544
|
-
var
|
|
10730
|
+
var NOT_BROADCAST_REFUSALS = /* @__PURE__ */ new Set([
|
|
10731
|
+
// 429
|
|
10545
10732
|
"batch_account_channel_capacity_exhausted",
|
|
10546
10733
|
"batch_channel_capacity_exhausted",
|
|
10547
|
-
"batch_deposit_rate_limited"
|
|
10734
|
+
"batch_deposit_rate_limited",
|
|
10735
|
+
"batch_deposit_ip_rate_limited",
|
|
10736
|
+
// 503
|
|
10737
|
+
"batch_signer_funding_headroom_exhausted",
|
|
10738
|
+
"batch_new_channels_disabled",
|
|
10739
|
+
"batch_public_new_channels_disabled",
|
|
10740
|
+
// 403
|
|
10741
|
+
"batch_account_admission_paused",
|
|
10742
|
+
"batch_channel_owner_mismatch",
|
|
10743
|
+
"batch_fee_payer_unavailable",
|
|
10744
|
+
"batch_fee_payer_not_dedicated",
|
|
10745
|
+
"batch_fee_payer_lane_not_authorized",
|
|
10746
|
+
"batch_transaction_fee_payer_mismatch",
|
|
10747
|
+
// 400
|
|
10748
|
+
"batch_initial_deposit_out_of_range",
|
|
10749
|
+
"batch_deposit_asset_not_supported",
|
|
10750
|
+
"batch_withdraw_delay_out_of_range"
|
|
10548
10751
|
]);
|
|
10549
10752
|
function provesNothingBroadcast(receipt) {
|
|
10550
|
-
return receipt?.success === false && !receipt.transaction && typeof receipt.errorReason === "string" &&
|
|
10753
|
+
return receipt?.success === false && !receipt.transaction && typeof receipt.errorReason === "string" && NOT_BROADCAST_REFUSALS.has(receipt.errorReason);
|
|
10754
|
+
}
|
|
10755
|
+
var CONFIG_REFUSALS = /* @__PURE__ */ new Set([
|
|
10756
|
+
"batch_initial_deposit_out_of_range",
|
|
10757
|
+
"batch_deposit_asset_not_supported",
|
|
10758
|
+
"batch_withdraw_delay_out_of_range"
|
|
10759
|
+
]);
|
|
10760
|
+
var REFUSAL_COOLDOWN_MS = 6e4;
|
|
10761
|
+
var CONFIG_REFUSAL_COOLDOWN_MS = 10 * 6e4;
|
|
10762
|
+
var REFUSAL_COOLDOWN_MAX_MS = 15 * 6e4;
|
|
10763
|
+
function refusalCooldownMs(errorReason, retryAfterMs2) {
|
|
10764
|
+
if (retryAfterMs2 !== void 0) return Math.min(retryAfterMs2, REFUSAL_COOLDOWN_MAX_MS);
|
|
10765
|
+
return errorReason !== void 0 && CONFIG_REFUSALS.has(errorReason) ? CONFIG_REFUSAL_COOLDOWN_MS : REFUSAL_COOLDOWN_MS;
|
|
10551
10766
|
}
|
|
10552
10767
|
function backoffDelay(attempt) {
|
|
10553
10768
|
const base = 1e3 * 2 ** Math.max(0, attempt - 1);
|
|
@@ -10680,7 +10895,7 @@ function intentTarget(intent) {
|
|
|
10680
10895
|
var SolanaBatchPayer = class {
|
|
10681
10896
|
constructor(init) {
|
|
10682
10897
|
this.init = init;
|
|
10683
|
-
const { operators, rateLimit } = init.options;
|
|
10898
|
+
const { operators, rateLimit, inDoubt } = init.options;
|
|
10684
10899
|
if (!Array.isArray(operators) || operators.length === 0 || operators.some((o) => typeof o !== "string" || !o)) {
|
|
10685
10900
|
throw new Error("batch.operators must list at least one base58 operator public key");
|
|
10686
10901
|
}
|
|
@@ -10692,11 +10907,16 @@ var SolanaBatchPayer = class {
|
|
|
10692
10907
|
if (!Number.isFinite(this.maxWaitMs) || this.maxWaitMs < 0) {
|
|
10693
10908
|
throw new Error("batch.rateLimit.maxWaitMs must be a finite number of ms >= 0");
|
|
10694
10909
|
}
|
|
10910
|
+
this.inDoubtMaxWaitMs = inDoubt?.maxWaitMs ?? DEFAULT_BATCH_IN_DOUBT.maxWaitMs;
|
|
10911
|
+
if (!Number.isFinite(this.inDoubtMaxWaitMs) || this.inDoubtMaxWaitMs < 0) {
|
|
10912
|
+
throw new Error("batch.inDoubt.maxWaitMs must be a finite number of ms >= 0");
|
|
10913
|
+
}
|
|
10695
10914
|
}
|
|
10696
10915
|
init;
|
|
10697
10916
|
state;
|
|
10698
10917
|
maxAttempts;
|
|
10699
10918
|
maxWaitMs;
|
|
10919
|
+
inDoubtMaxWaitMs;
|
|
10700
10920
|
counters = {
|
|
10701
10921
|
fallbacks: 0,
|
|
10702
10922
|
fallbacksByReason: {},
|
|
@@ -11065,14 +11285,19 @@ var SolanaBatchPayer = class {
|
|
|
11065
11285
|
* the call's result (`served`, nothing paid); one that fails otherwise
|
|
11066
11286
|
* is the call's error (`failed`, `"unpaid"`), never an exact payment
|
|
11067
11287
|
* against the stale challenge;
|
|
11068
|
-
* - in doubt: ends only with a definitive success receipt from
|
|
11069
|
-
*
|
|
11070
|
-
*
|
|
11071
|
-
*
|
|
11072
|
-
*
|
|
11288
|
+
* - in doubt: ends only with a definitive success receipt from a replay
|
|
11289
|
+
* of the identical payment ({@link resolveInDoubt}: the one replay a
|
|
11290
|
+
* receipt-less 429 is allowed, or the replays of a payment carrying a
|
|
11291
|
+
* payment identifier while the gateway says one can succeed, within
|
|
11292
|
+
* `inDoubt.maxWaitMs`), or a thrown {@link BatchPaymentUnresolvedError}.
|
|
11293
|
+
* No new authorization, no new deposit, no exact payment and no fallback
|
|
11294
|
+
* model is ever paid for a call in doubt.
|
|
11073
11295
|
*
|
|
11074
11296
|
* The wallet stays in cooldown after a 429, so its other calls wait
|
|
11075
|
-
* instead of sending opens of their own.
|
|
11297
|
+
* instead of sending opens of their own. After a deposit the facilitator
|
|
11298
|
+
* refused before broadcasting it (a 402 with a failed `batch_*` admission
|
|
11299
|
+
* receipt), its calls do not wait: they pay exact at once
|
|
11300
|
+
* (`admission_refused`) until the refusal cooldown ends.
|
|
11076
11301
|
*
|
|
11077
11302
|
* @param closesAtStart - {@link closeFence}, taken before the call's first
|
|
11078
11303
|
* (unpaid) request. If a close completed since, the call pays exact
|
|
@@ -11136,6 +11361,15 @@ var SolanaBatchPayer = class {
|
|
|
11136
11361
|
try {
|
|
11137
11362
|
for (; ; ) {
|
|
11138
11363
|
const now = Date.now();
|
|
11364
|
+
if ((wallet.refusedUntil ?? 0) > now) {
|
|
11365
|
+
return fallback({
|
|
11366
|
+
reason: "admission_refused",
|
|
11367
|
+
errorReason: wallet.refusedFor,
|
|
11368
|
+
retryAfterMs: (wallet.refusedUntil ?? 0) - now,
|
|
11369
|
+
attempt: budget.attempt,
|
|
11370
|
+
detail: `the facilitator refused this wallet's last batch deposit (${wallet.refusedFor ?? "unknown"}); batch is skipped until the refusal cooldown ends`
|
|
11371
|
+
});
|
|
11372
|
+
}
|
|
11139
11373
|
if (wallet.cooldownUntil > now && wallet.cooldownUntil !== waitedUntil) {
|
|
11140
11374
|
const wait = wallet.cooldownUntil - now;
|
|
11141
11375
|
if (budget.waited + wait > this.maxWaitMs) {
|
|
@@ -11225,12 +11459,23 @@ var SolanaBatchPayer = class {
|
|
|
11225
11459
|
if (built.kind !== "authorization") {
|
|
11226
11460
|
if (outcome.depositSafe) this.forgetIntent(wallet, built.key);
|
|
11227
11461
|
else this.distrust(wallet, built, "deposit_refused");
|
|
11462
|
+
if (outcome.refusedForMs !== void 0) {
|
|
11463
|
+
const until = Date.now() + outcome.refusedForMs;
|
|
11464
|
+
if (until > (wallet.refusedUntil ?? 0)) {
|
|
11465
|
+
wallet.refusedUntil = until;
|
|
11466
|
+
wallet.refusedFor = outcome.errorReason;
|
|
11467
|
+
}
|
|
11468
|
+
}
|
|
11228
11469
|
}
|
|
11229
11470
|
sent = void 0;
|
|
11230
11471
|
release();
|
|
11231
11472
|
if (!outcome.rateLimited) {
|
|
11232
|
-
const { kind: _kind, rateLimited: _limited, depositSafe: _safe, ...event } = outcome;
|
|
11233
|
-
return fallback({
|
|
11473
|
+
const { kind: _kind, rateLimited: _limited, depositSafe: _safe, refusedForMs, ...event } = outcome;
|
|
11474
|
+
return fallback({
|
|
11475
|
+
...event,
|
|
11476
|
+
...refusedForMs !== void 0 && built.kind !== "authorization" ? { retryAfterMs: refusedForMs } : {},
|
|
11477
|
+
attempt: budget.attempt
|
|
11478
|
+
});
|
|
11234
11479
|
}
|
|
11235
11480
|
const wait = this.coolDown(wallet, outcome.rateLimited.retryAfterMs ?? backoffDelay(budget.attempt));
|
|
11236
11481
|
if (budget.attempt >= this.maxAttempts || budget.waited + wait > this.maxWaitMs) {
|
|
@@ -11276,7 +11521,7 @@ var SolanaBatchPayer = class {
|
|
|
11276
11521
|
if (built.kind !== "authorization" && !(built.key && wallet.resyncs.has(built.key))) this.forgetIntent(wallet, built.key);
|
|
11277
11522
|
sent = void 0;
|
|
11278
11523
|
release();
|
|
11279
|
-
await this.report({ type: "recovered", reason: "rate_limited", attempt: budget.attempt });
|
|
11524
|
+
await this.report({ type: "recovered", reason: outcome.status === 429 ? "rate_limited" : "in_doubt", attempt: budget.attempt });
|
|
11280
11525
|
return { kind: "paid", response: resolved.response, chargedUsd: resolved.chargedUsd };
|
|
11281
11526
|
}
|
|
11282
11527
|
return await this.raiseUnresolved(wallet, built, resolved, budget.attempt);
|
|
@@ -11325,21 +11570,24 @@ var SolanaBatchPayer = class {
|
|
|
11325
11570
|
/**
|
|
11326
11571
|
* Try to end a payment in doubt with the gateway's own word.
|
|
11327
11572
|
*
|
|
11328
|
-
* The one source of evidence this SDK accepts
|
|
11329
|
-
*
|
|
11330
|
-
*
|
|
11331
|
-
*
|
|
11332
|
-
*
|
|
11333
|
-
*
|
|
11573
|
+
* The one source of evidence this SDK accepts is a definitive success
|
|
11574
|
+
* receipt on a byte-identical replay of the payment. Any other answer to a
|
|
11575
|
+
* replay (a 402 or `duplicate_settlement`, which mean the original reached
|
|
11576
|
+
* the gateway; a 409; another 429; a 5xx; a timeout) leaves it in doubt.
|
|
11577
|
+
*
|
|
11578
|
+
* - A payment carrying a payment identifier is replayed for as long as
|
|
11579
|
+
* the gateway says a later replay can succeed ({@link replayInDoubt}).
|
|
11580
|
+
* - Any other payment gets a replay only after a 429 without a receipt:
|
|
11581
|
+
* one, after its backoff (the owner's bounded safety attempt), within
|
|
11582
|
+
* `rateLimit`. Every other in-doubt outcome is never replayed.
|
|
11334
11583
|
*
|
|
11335
11584
|
* Chain state is not evidence here: it can settle a deposit (see
|
|
11336
|
-
* `resync`), never whether the gateway charged.
|
|
11337
|
-
* receipt on every response, a request-status endpoint with a fence)
|
|
11338
|
-
* plugs in here, before the caller raises.
|
|
11585
|
+
* `resync`), never whether the gateway charged.
|
|
11339
11586
|
*
|
|
11340
11587
|
* @returns the charged replay, or the doubt to raise.
|
|
11341
11588
|
*/
|
|
11342
11589
|
async resolveInDoubt(wallet, sent, doubt, send, budget) {
|
|
11590
|
+
if (hasPaymentIdentifier(sent.payload)) return this.replayInDoubt(wallet, sent, doubt, send, budget.attempt);
|
|
11343
11591
|
if (!doubt.replayable) return doubt;
|
|
11344
11592
|
const wait = this.coolDown(wallet, doubt.retryAfterMs ?? backoffDelay(budget.attempt));
|
|
11345
11593
|
if (budget.attempt >= this.maxAttempts || budget.waited + wait > this.maxWaitMs) {
|
|
@@ -11359,9 +11607,94 @@ var SolanaBatchPayer = class {
|
|
|
11359
11607
|
budget.waited += wait;
|
|
11360
11608
|
await sleep2(wait);
|
|
11361
11609
|
budget.attempt += 1;
|
|
11362
|
-
|
|
11363
|
-
|
|
11364
|
-
|
|
11610
|
+
return replayDoubt(await this.sendOnce(wallet, sent, send, true, true));
|
|
11611
|
+
}
|
|
11612
|
+
/**
|
|
11613
|
+
* Replay a payment in doubt that carries a payment identifier until the
|
|
11614
|
+
* gateway's answer settles it.
|
|
11615
|
+
*
|
|
11616
|
+
* A gateway that declares `payment-identifier` stores each paid response
|
|
11617
|
+
* under its identifier and answers a byte-identical replay of a completed
|
|
11618
|
+
* request with it, the original receipt included; while the original is
|
|
11619
|
+
* still running, it answers 409 with a `Retry-After`. So the identical
|
|
11620
|
+
* payment (the same `PAYMENT-SIGNATURE` header and the same body) is
|
|
11621
|
+
* replayed for as long as the last answer says a later one can succeed
|
|
11622
|
+
* ({@link replayMayResolve}), after {@link inDoubtWait} each time, as long
|
|
11623
|
+
* as the wait fits in `inDoubt.maxWaitMs`, counted from the first doubt:
|
|
11624
|
+
* every wait (as long as it really took) and every replay's round trip. A
|
|
11625
|
+
* replay already sent is not cut short. These replays do not count
|
|
11626
|
+
* against `rateLimit`; a 429 among them still puts the wallet in cooldown.
|
|
11627
|
+
*
|
|
11628
|
+
* Every answer that does not settle it is held back from the scheme
|
|
11629
|
+
* ({@link InDoubt.held}) and handed over only once no replay follows, so
|
|
11630
|
+
* the scheme rolls the payment back once, or takes the receipt that answer
|
|
11631
|
+
* carries. That is the last answer, unless an earlier one carried a
|
|
11632
|
+
* success receipt and the later ones do not: a success receipt is the
|
|
11633
|
+
* gateway's word that it charged, and handing the scheme a receipt-less
|
|
11634
|
+
* refusal of a replay instead would roll a real charge back and desync the
|
|
11635
|
+
* channel's cumulative.
|
|
11636
|
+
*
|
|
11637
|
+
* @returns the charged replay, or the doubt to raise.
|
|
11638
|
+
*/
|
|
11639
|
+
async replayInDoubt(wallet, sent, first, send, attempt) {
|
|
11640
|
+
let doubt = first;
|
|
11641
|
+
let held = first.held;
|
|
11642
|
+
let heldSucceeded = held !== void 0 && receiptSucceeded(sent.http, held);
|
|
11643
|
+
let replays = 0;
|
|
11644
|
+
let spent = 0;
|
|
11645
|
+
const stop = async (note) => {
|
|
11646
|
+
if (!doubt.settled && held) {
|
|
11647
|
+
const answer = held;
|
|
11648
|
+
await this.settle(sent.http, sent.payload, (name) => answer.headers.get(name), answer.status);
|
|
11649
|
+
}
|
|
11650
|
+
const notes = [replays > 0 ? `${replays} replay${replays === 1 ? "" : "s"} did not resolve it` : "", note ?? ""];
|
|
11651
|
+
return {
|
|
11652
|
+
...doubt,
|
|
11653
|
+
settled: doubt.settled || held !== void 0,
|
|
11654
|
+
held: void 0,
|
|
11655
|
+
firstReason: first.reason,
|
|
11656
|
+
detail: [doubt.detail, ...notes].filter(Boolean).join("; ")
|
|
11657
|
+
};
|
|
11658
|
+
};
|
|
11659
|
+
for (; ; ) {
|
|
11660
|
+
if (!replayMayResolve(doubt)) {
|
|
11661
|
+
return stop(doubt.held?.status === 409 ? "the gateway sent no Retry-After, so no replay can resolve it" : void 0);
|
|
11662
|
+
}
|
|
11663
|
+
const wait = inDoubtWait(doubt, replays + 1);
|
|
11664
|
+
const status2 = doubt.held?.status;
|
|
11665
|
+
if (status2 === 429) this.coolDown(wallet, (doubt.held && parseRetryAfter(doubt.held.headers.get("retry-after"))) ?? wait);
|
|
11666
|
+
if (spent + wait > this.inDoubtMaxWaitMs) {
|
|
11667
|
+
return stop(`batch.inDoubt.maxWaitMs (${this.inDoubtMaxWaitMs} ms) leaves no room for ${replays > 0 ? "another replay" : "a replay"}`);
|
|
11668
|
+
}
|
|
11669
|
+
await this.report({
|
|
11670
|
+
type: "backoff",
|
|
11671
|
+
reason: status2 === 429 ? "rate_limited" : "in_doubt",
|
|
11672
|
+
status: status2,
|
|
11673
|
+
errorReason: doubt.errorReason,
|
|
11674
|
+
retryAfterMs: wait,
|
|
11675
|
+
attempt,
|
|
11676
|
+
detail: `replay ${replays + 1} of the payment in doubt`
|
|
11677
|
+
});
|
|
11678
|
+
const sleptFrom = Date.now();
|
|
11679
|
+
await sleep2(wait);
|
|
11680
|
+
spent += Math.max(wait, Date.now() - sleptFrom);
|
|
11681
|
+
replays += 1;
|
|
11682
|
+
const startedAt = Date.now();
|
|
11683
|
+
const replayed = replayDoubt(await this.sendOnce(wallet, sent, send, true, true));
|
|
11684
|
+
spent += Math.max(0, Date.now() - startedAt);
|
|
11685
|
+
if (replayed.kind === "charged") return replayed;
|
|
11686
|
+
doubt = replayed;
|
|
11687
|
+
if (doubt.held) {
|
|
11688
|
+
const succeeded = receiptSucceeded(sent.http, doubt.held);
|
|
11689
|
+
if (succeeded || !heldSucceeded) {
|
|
11690
|
+
held = doubt.held;
|
|
11691
|
+
heldSucceeded = succeeded;
|
|
11692
|
+
}
|
|
11693
|
+
} else if (doubt.settled) {
|
|
11694
|
+
held = void 0;
|
|
11695
|
+
heldSucceeded = false;
|
|
11696
|
+
}
|
|
11697
|
+
}
|
|
11365
11698
|
}
|
|
11366
11699
|
/**
|
|
11367
11700
|
* Raise a payment in doubt: release the scheme's hold on it (the channel
|
|
@@ -11372,10 +11705,11 @@ var SolanaBatchPayer = class {
|
|
|
11372
11705
|
async raiseUnresolved(wallet, sent, doubt, attempt) {
|
|
11373
11706
|
if (!doubt.settled) await this.settle(sent.http, sent.payload, () => null, 0);
|
|
11374
11707
|
if (sent.kind !== "authorization") {
|
|
11708
|
+
const reason = doubt.firstReason ?? doubt.reason;
|
|
11375
11709
|
this.distrust(
|
|
11376
11710
|
wallet,
|
|
11377
11711
|
sent,
|
|
11378
|
-
|
|
11712
|
+
reason === "no_response" ? "deposit_unanswered" : reason === "replay_unresolved" ? "deposit_rate_limited" : "deposit_failed"
|
|
11379
11713
|
);
|
|
11380
11714
|
}
|
|
11381
11715
|
await this.report({
|
|
@@ -11462,8 +11796,8 @@ var SolanaBatchPayer = class {
|
|
|
11462
11796
|
* here, and only for a first send.
|
|
11463
11797
|
*
|
|
11464
11798
|
* @param retry - count this send in `retries` (it follows this call's own 429).
|
|
11465
|
-
* @param replay - this is
|
|
11466
|
-
*
|
|
11799
|
+
* @param replay - this is a replay of a payment in doubt: only a 2xx with
|
|
11800
|
+
* a success receipt counts, everything else stays in doubt.
|
|
11467
11801
|
*/
|
|
11468
11802
|
async sendOnce(wallet, sent, send, retry, replay) {
|
|
11469
11803
|
if (retry) this.counters.retries += 1;
|
|
@@ -11477,6 +11811,7 @@ var SolanaBatchPayer = class {
|
|
|
11477
11811
|
reason: replay ? "replay_unresolved" : "no_response",
|
|
11478
11812
|
replayable: false,
|
|
11479
11813
|
settled: false,
|
|
11814
|
+
unanswered: true,
|
|
11480
11815
|
detail: `${replay ? "the replay" : "the payment"} got no answer (${err instanceof Error ? `${err.name}: ${err.message}` : String(err)})`,
|
|
11481
11816
|
cause: err
|
|
11482
11817
|
};
|
|
@@ -11501,6 +11836,7 @@ var SolanaBatchPayer = class {
|
|
|
11501
11836
|
const getHeader = (name) => response.headers.get(name);
|
|
11502
11837
|
const receipted = Boolean(getHeader("PAYMENT-RESPONSE") || getHeader("X-PAYMENT-RESPONSE"));
|
|
11503
11838
|
const status2 = response.status;
|
|
11839
|
+
const hold = hasPaymentIdentifier(payload) && (!response.ok || replay && peekReceipt(http, getHeader)?.success !== true);
|
|
11504
11840
|
if (status2 === 429 && !receipted) {
|
|
11505
11841
|
return {
|
|
11506
11842
|
kind: "in_doubt",
|
|
@@ -11510,12 +11846,27 @@ var SolanaBatchPayer = class {
|
|
|
11510
11846
|
status: status2,
|
|
11511
11847
|
retryAfterMs: parseRetryAfter(getHeader("retry-after")),
|
|
11512
11848
|
errorReason: await rateLimitReason(response, void 0),
|
|
11513
|
-
detail: replay ? "the replay was answered 429 without a receipt" : "a 429 without a receipt"
|
|
11849
|
+
detail: replay ? "the replay was answered 429 without a receipt" : "a 429 without a receipt",
|
|
11850
|
+
...hold ? { held: response } : {}
|
|
11514
11851
|
};
|
|
11515
11852
|
}
|
|
11853
|
+
const outcome = await this.classifyAnswer(wallet, sent, response, replay, hold);
|
|
11854
|
+
if (!hold) return outcome;
|
|
11855
|
+
if (outcome.kind === "in_doubt") return { ...outcome, settled: false, held: response };
|
|
11856
|
+
await this.settle(http, payload, getHeader, status2);
|
|
11857
|
+
return outcome;
|
|
11858
|
+
}
|
|
11859
|
+
/**
|
|
11860
|
+
* {@link classify}, past a receipt-less 429. With `hold`, the answer is not
|
|
11861
|
+
* handed to the scheme: its receipt is only read.
|
|
11862
|
+
*/
|
|
11863
|
+
async classifyAnswer(wallet, sent, response, replay, hold) {
|
|
11864
|
+
const { http, payload } = sent;
|
|
11865
|
+
const getHeader = (name) => response.headers.get(name);
|
|
11866
|
+
const status2 = response.status;
|
|
11516
11867
|
const channelId = payloadChannelId(payload);
|
|
11517
11868
|
const before = channelId ? confirmedState(wallet.book.find(channelId)?.record) : void 0;
|
|
11518
|
-
const reconciled = await this.settle(http, payload, getHeader, status2);
|
|
11869
|
+
const reconciled = hold ? { settleResponse: peekReceipt(http, getHeader), ok: false } : await this.settle(http, payload, getHeader, status2);
|
|
11519
11870
|
const receipt = reconciled.settleResponse;
|
|
11520
11871
|
const definitive = receipt?.success === true && reconciled.ok && recordAdvanced(before, channelId ? confirmedState(wallet.book.find(channelId)?.record) : void 0, payload, receipt);
|
|
11521
11872
|
if (!response.ok && !replay && sent.kind === "authorization" && cancelledReceipt(receipt)) {
|
|
@@ -11578,7 +11929,15 @@ var SolanaBatchPayer = class {
|
|
|
11578
11929
|
cause: await afterPaymentError(response)
|
|
11579
11930
|
};
|
|
11580
11931
|
}
|
|
11581
|
-
|
|
11932
|
+
const refused = provesNothingBroadcast(receipt);
|
|
11933
|
+
return {
|
|
11934
|
+
kind: "not_charged",
|
|
11935
|
+
reason: "payment_required",
|
|
11936
|
+
status: status2,
|
|
11937
|
+
errorReason: receipt?.errorReason,
|
|
11938
|
+
depositSafe: refused,
|
|
11939
|
+
...refused ? { refusedForMs: refusalCooldownMs(receipt?.errorReason, parseRetryAfter(getHeader("retry-after"))) } : {}
|
|
11940
|
+
};
|
|
11582
11941
|
}
|
|
11583
11942
|
const refusal = await batchRefusal(response);
|
|
11584
11943
|
if (refusal) {
|
|
@@ -11609,7 +11968,7 @@ var SolanaBatchPayer = class {
|
|
|
11609
11968
|
const http = (await this.build(wallet)).http;
|
|
11610
11969
|
try {
|
|
11611
11970
|
await this.prepareTopUp(wallet, paymentRequired);
|
|
11612
|
-
return { http, payload: await http.createPaymentPayload(withoutClientSignedBatch(paymentRequired)) };
|
|
11971
|
+
return { http, payload: await http.createPaymentPayload(withPaymentIdentifier(withoutClientSignedBatch(paymentRequired))) };
|
|
11613
11972
|
} catch (err) {
|
|
11614
11973
|
if (pass > 0 || !isResyncRequired(err) || wallet.resyncs.size === 0) throw err;
|
|
11615
11974
|
} finally {
|
|
@@ -11702,13 +12061,7 @@ var SolanaBatchPayer = class {
|
|
|
11702
12061
|
console.error(
|
|
11703
12062
|
`[@blockrun/llm] batch-settlement receipt not reconciled: ${JSON.stringify(err instanceof Error ? err.message : String(err))}`
|
|
11704
12063
|
);
|
|
11705
|
-
|
|
11706
|
-
try {
|
|
11707
|
-
settleResponse = http.getPaymentSettleResponse(getHeader);
|
|
11708
|
-
} catch {
|
|
11709
|
-
settleResponse = void 0;
|
|
11710
|
-
}
|
|
11711
|
-
return { settleResponse, ok: false };
|
|
12064
|
+
return { settleResponse: peekReceipt(http, getHeader), ok: false };
|
|
11712
12065
|
}
|
|
11713
12066
|
}
|
|
11714
12067
|
/**
|
|
@@ -12091,9 +12444,9 @@ var SolanaLLMClient = class {
|
|
|
12091
12444
|
};
|
|
12092
12445
|
if (options?.temperature !== void 0) body.temperature = options.temperature;
|
|
12093
12446
|
if (options?.topP !== void 0) body.top_p = options.topP;
|
|
12094
|
-
if (options?.searchParameters !== void 0) body.search_parameters = options.searchParameters;
|
|
12447
|
+
if (options?.searchParameters !== void 0) body.search_parameters = toWireSearchParameters(options.searchParameters);
|
|
12095
12448
|
else if (options?.search === true) body.search_parameters = { mode: "on" };
|
|
12096
|
-
if (options?.tools !== void 0) body.tools = options.tools;
|
|
12449
|
+
if (options?.tools !== void 0) body.tools = toWireTools(options.tools);
|
|
12097
12450
|
if (options?.toolChoice !== void 0) body.tool_choice = options.toolChoice;
|
|
12098
12451
|
if (options?.responseFormat !== void 0) body.response_format = options.responseFormat;
|
|
12099
12452
|
if (options?.stop !== void 0) body.stop = options.stop;
|
|
@@ -12104,7 +12457,7 @@ var SolanaLLMClient = class {
|
|
|
12104
12457
|
for (let index = 0; index < chain.length; index += 1) {
|
|
12105
12458
|
const candidate = chain[index];
|
|
12106
12459
|
try {
|
|
12107
|
-
return await this.requestWithPayment("/v1/chat/completions", buildBody(candidate));
|
|
12460
|
+
return await this.requestWithPayment("/v1/chat/completions", buildBody(candidate), options?.timeout);
|
|
12108
12461
|
} catch (error) {
|
|
12109
12462
|
lastError = error;
|
|
12110
12463
|
if (!isTransientError(error) || index === chain.length - 1) throw error;
|
|
@@ -12154,6 +12507,8 @@ var SolanaLLMClient = class {
|
|
|
12154
12507
|
{
|
|
12155
12508
|
routingProfile: options?.routingProfile,
|
|
12156
12509
|
requiresStructuredOutput: options?.responseFormat !== void 0,
|
|
12510
|
+
search: options?.search,
|
|
12511
|
+
searchParameters: options?.searchParameters,
|
|
12157
12512
|
minimumPaymentUsd: this.apiAuth ? 0 : SOLANA_MINIMUM_PAYMENT_USD
|
|
12158
12513
|
}
|
|
12159
12514
|
);
|
|
@@ -12181,6 +12536,8 @@ var SolanaLLMClient = class {
|
|
|
12181
12536
|
requiresStructuredOutput: options.responseFormat !== void 0,
|
|
12182
12537
|
tools: options.tools,
|
|
12183
12538
|
toolChoice: options.toolChoice,
|
|
12539
|
+
search: options.search,
|
|
12540
|
+
searchParameters: options.searchParameters,
|
|
12184
12541
|
conversationChars,
|
|
12185
12542
|
hasVision,
|
|
12186
12543
|
minimumPaymentUsd: this.apiAuth ? 0 : SOLANA_MINIMUM_PAYMENT_USD
|
|
@@ -12196,6 +12553,7 @@ var SolanaLLMClient = class {
|
|
|
12196
12553
|
toolChoice: options.toolChoice,
|
|
12197
12554
|
responseFormat: options.responseFormat,
|
|
12198
12555
|
stop: options.stop,
|
|
12556
|
+
timeout: options.timeout,
|
|
12199
12557
|
// An explicit caller-supplied chain wins over the routed one.
|
|
12200
12558
|
fallbackModels: options.fallbackModels ?? decision.fallbacks
|
|
12201
12559
|
});
|
|
@@ -12333,7 +12691,7 @@ var SolanaLLMClient = class {
|
|
|
12333
12691
|
*
|
|
12334
12692
|
* @returns USDC balance as a float
|
|
12335
12693
|
*/
|
|
12336
|
-
async getBalance() {
|
|
12694
|
+
async getBalance(options) {
|
|
12337
12695
|
const usdc_mint = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
12338
12696
|
const address = await this.getWalletAddress();
|
|
12339
12697
|
try {
|
|
@@ -12348,6 +12706,9 @@ var SolanaLLMClient = class {
|
|
|
12348
12706
|
})
|
|
12349
12707
|
});
|
|
12350
12708
|
const data = await response.json();
|
|
12709
|
+
if (options?.strict && (!response.ok || data.error)) {
|
|
12710
|
+
throw new Error(`Solana RPC balance lookup failed: ${data.error?.message ?? `HTTP ${response.status}`}`);
|
|
12711
|
+
}
|
|
12351
12712
|
const accounts = data.result?.value || [];
|
|
12352
12713
|
if (!accounts.length) return 0;
|
|
12353
12714
|
let total = 0;
|
|
@@ -12355,7 +12716,8 @@ var SolanaLLMClient = class {
|
|
|
12355
12716
|
total += acct.account?.data?.parsed?.info?.tokenAmount?.uiAmount || 0;
|
|
12356
12717
|
}
|
|
12357
12718
|
return total;
|
|
12358
|
-
} catch {
|
|
12719
|
+
} catch (error) {
|
|
12720
|
+
if (options?.strict) throw error;
|
|
12359
12721
|
return 0;
|
|
12360
12722
|
}
|
|
12361
12723
|
}
|
|
@@ -12630,15 +12992,16 @@ var SolanaLLMClient = class {
|
|
|
12630
12992
|
* `"paid-or-in-doubt"`, and so is any error with an API key once the billed
|
|
12631
12993
|
* request was sent, except the account API's explicit 4xx refusal.
|
|
12632
12994
|
*/
|
|
12633
|
-
async requestWithPayment(endpoint, body) {
|
|
12995
|
+
async requestWithPayment(endpoint, body, timeoutMs) {
|
|
12634
12996
|
const url = `${this.apiUrl}${endpoint}`;
|
|
12997
|
+
const json = JSON.stringify(body);
|
|
12635
12998
|
const closesAtStart = await this.batchPayer?.closeFence();
|
|
12636
12999
|
for (let staleRetries = 0; ; ) {
|
|
12637
13000
|
const response = await this.sendUnpaid(url, {
|
|
12638
13001
|
method: "POST",
|
|
12639
13002
|
headers: { "Content-Type": "application/json", "User-Agent": USER_AGENT },
|
|
12640
|
-
body:
|
|
12641
|
-
});
|
|
13003
|
+
body: json
|
|
13004
|
+
}, timeoutMs);
|
|
12642
13005
|
if (response.status === 402) {
|
|
12643
13006
|
try {
|
|
12644
13007
|
let paymentRequired;
|
|
@@ -12654,16 +13017,16 @@ var SolanaLLMClient = class {
|
|
|
12654
13017
|
(paymentHeaders) => this.fetchWithTimeout(url, {
|
|
12655
13018
|
method: "POST",
|
|
12656
13019
|
headers: { "Content-Type": "application/json", "User-Agent": USER_AGENT, ...paymentHeaders },
|
|
12657
|
-
body:
|
|
12658
|
-
}),
|
|
13020
|
+
body: json
|
|
13021
|
+
}, timeoutMs),
|
|
12659
13022
|
// A fresh, unpaid challenge after a 429 wait: the request itself,
|
|
12660
13023
|
// sent again without a payment. Every error it throws is unpaid.
|
|
12661
13024
|
async () => {
|
|
12662
13025
|
const challenge = await this.sendUnpaid(url, {
|
|
12663
13026
|
method: "POST",
|
|
12664
13027
|
headers: { "Content-Type": "application/json", "User-Agent": USER_AGENT },
|
|
12665
|
-
body:
|
|
12666
|
-
});
|
|
13028
|
+
body: json
|
|
13029
|
+
}, timeoutMs);
|
|
12667
13030
|
if (challenge.status === 402) {
|
|
12668
13031
|
try {
|
|
12669
13032
|
return { kind: "challenge", paymentRequired: await this.readPaymentRequired(challenge) };
|
|
@@ -12678,13 +13041,14 @@ var SolanaLLMClient = class {
|
|
|
12678
13041
|
);
|
|
12679
13042
|
if (batch.kind === "paid") {
|
|
12680
13043
|
this.recordSettlement(batch.chargedUsd);
|
|
12681
|
-
|
|
13044
|
+
const paid = await this.readPaidJson(batch.response);
|
|
13045
|
+
return withCost(paid, batch.response, batch.chargedUsd, "batch");
|
|
12682
13046
|
}
|
|
12683
|
-
if (batch.kind === "served") return await this.readUnpaidJson(batch.response);
|
|
13047
|
+
if (batch.kind === "served") return this.withUnpaidCost(await this.readUnpaidJson(batch.response));
|
|
12684
13048
|
if (batch.kind === "failed") throw batch.error;
|
|
12685
13049
|
exactRequired = batch.paymentRequired ?? paymentRequired;
|
|
12686
13050
|
}
|
|
12687
|
-
return await this.handlePaymentAndRetry(url,
|
|
13051
|
+
return await this.handlePaymentAndRetry(url, json, exactRequired, staleRetries > 0, timeoutMs);
|
|
12688
13052
|
} catch (error) {
|
|
12689
13053
|
if (!(error instanceof SafeStaleBlockhashError) || staleRetries >= STALE_BLOCKHASH_RETRY_BACKOFFS_MS.length) throw error;
|
|
12690
13054
|
await waitForStaleRetry(staleRetries++);
|
|
@@ -12692,9 +13056,21 @@ var SolanaLLMClient = class {
|
|
|
12692
13056
|
}
|
|
12693
13057
|
}
|
|
12694
13058
|
if (!response.ok) throw await unpaidApiError(response);
|
|
12695
|
-
return await this.readUnpaidJson(response);
|
|
13059
|
+
return this.withUnpaidCost(await this.readUnpaidJson(response));
|
|
12696
13060
|
}
|
|
12697
13061
|
}
|
|
13062
|
+
/**
|
|
13063
|
+
* Cost of a chat response served without a payment: 0 with a wallet (a free
|
|
13064
|
+
* model). With an API key the call is billed to the account, so costUsd stays
|
|
13065
|
+
* undefined (see the dashboard).
|
|
13066
|
+
*/
|
|
13067
|
+
withUnpaidCost(served) {
|
|
13068
|
+
if (!this.apiAuth) {
|
|
13069
|
+
served.costUsd = 0;
|
|
13070
|
+
served.settlement = { scheme: "free", quotedUsd: 0 };
|
|
13071
|
+
}
|
|
13072
|
+
return served;
|
|
13073
|
+
}
|
|
12698
13074
|
/**
|
|
12699
13075
|
* Read the JSON body of a 2xx served without a payment: the free tier, or
|
|
12700
13076
|
* with an API key, where this very request was the billed one.
|
|
@@ -12711,9 +13087,9 @@ var SolanaLLMClient = class {
|
|
|
12711
13087
|
* {@link RetryDisposition}: `"unpaid"` with a wallet; with an API key, see
|
|
12712
13088
|
* {@link accountErrorDisposition} (the request itself is billed).
|
|
12713
13089
|
*/
|
|
12714
|
-
async sendUnpaid(url, init) {
|
|
13090
|
+
async sendUnpaid(url, init, timeoutMs) {
|
|
12715
13091
|
try {
|
|
12716
|
-
return await this.fetchWithTimeout(url, init);
|
|
13092
|
+
return await this.fetchWithTimeout(url, init, timeoutMs);
|
|
12717
13093
|
} catch (error) {
|
|
12718
13094
|
throw withDisposition(error, this.apiAuth ? accountErrorDisposition(error) : "unpaid");
|
|
12719
13095
|
}
|
|
@@ -12982,7 +13358,7 @@ var SolanaLLMClient = class {
|
|
|
12982
13358
|
if (!feePayer) throw new PaymentError("Missing feePayer in 402 extra field");
|
|
12983
13359
|
const fromAddress = await this.getWalletAddress();
|
|
12984
13360
|
const secretKey = await solanaKeyToBytes(this.privateKey);
|
|
12985
|
-
const extensions = paymentRequired.extensions;
|
|
13361
|
+
const extensions = withPaymentIdentifier(paymentRequired).extensions;
|
|
12986
13362
|
const paymentPayload = await createSolanaPaymentPayload(
|
|
12987
13363
|
secretKey,
|
|
12988
13364
|
fromAddress,
|
|
@@ -13005,7 +13381,7 @@ var SolanaLLMClient = class {
|
|
|
13005
13381
|
);
|
|
13006
13382
|
return { paymentPayload, costUsd: parseFloat(details.amount) / 1e6 };
|
|
13007
13383
|
}
|
|
13008
|
-
async handlePaymentAndRetry(url,
|
|
13384
|
+
async handlePaymentAndRetry(url, json, paymentRequired, forceFreshBlockhash = false, timeoutMs) {
|
|
13009
13385
|
let signed;
|
|
13010
13386
|
try {
|
|
13011
13387
|
signed = await this.signExactPayment(url, paymentRequired, forceFreshBlockhash, `${this.apiUrl}/v1/chat/completions`);
|
|
@@ -13022,14 +13398,15 @@ var SolanaLLMClient = class {
|
|
|
13022
13398
|
"User-Agent": USER_AGENT,
|
|
13023
13399
|
"PAYMENT-SIGNATURE": paymentPayload
|
|
13024
13400
|
},
|
|
13025
|
-
body:
|
|
13026
|
-
});
|
|
13401
|
+
body: json
|
|
13402
|
+
}, timeoutMs);
|
|
13027
13403
|
} catch (error) {
|
|
13028
13404
|
throw withDisposition(error, "paid-or-in-doubt");
|
|
13029
13405
|
}
|
|
13030
13406
|
await this.assertPaid(retryResponse);
|
|
13031
13407
|
this.recordSettlement(costUsd);
|
|
13032
|
-
|
|
13408
|
+
const paid = await this.readPaidJson(retryResponse);
|
|
13409
|
+
return withCost(paid, retryResponse, costUsd, "exact");
|
|
13033
13410
|
}
|
|
13034
13411
|
async requestWithPaymentRaw(endpoint, body, media = {}) {
|
|
13035
13412
|
const url = `${this.apiUrl}${endpoint}`;
|
|
@@ -13745,6 +14122,7 @@ var AnthropicClient = class {
|
|
|
13745
14122
|
BatchPaymentUnresolvedError,
|
|
13746
14123
|
BlockrunClient,
|
|
13747
14124
|
BlockrunError,
|
|
14125
|
+
DEFAULT_BATCH_IN_DOUBT,
|
|
13748
14126
|
DEFAULT_BATCH_RATE_LIMIT,
|
|
13749
14127
|
EVM_NETWORKS,
|
|
13750
14128
|
ImageClient,
|
|
@@ -13807,6 +14185,7 @@ var AnthropicClient = class {
|
|
|
13807
14185
|
loadWallet,
|
|
13808
14186
|
logCost,
|
|
13809
14187
|
parsePaymentRequired,
|
|
14188
|
+
readSettlement,
|
|
13810
14189
|
retryDisposition,
|
|
13811
14190
|
saveSolanaWallet,
|
|
13812
14191
|
saveToCache,
|
|
@@ -13821,6 +14200,8 @@ var AnthropicClient = class {
|
|
|
13821
14200
|
solanaKeyToBytes,
|
|
13822
14201
|
solanaPublicKey,
|
|
13823
14202
|
status,
|
|
14203
|
+
toWireSearchParameters,
|
|
14204
|
+
toWireTools,
|
|
13824
14205
|
validateMaxTokens,
|
|
13825
14206
|
validateModel,
|
|
13826
14207
|
validateTemperature,
|