@blockrun/llm 1.4.2 → 1.5.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 +9 -1
- package/dist/index.cjs +445 -36662
- package/dist/index.d.cts +110 -11
- package/dist/index.d.ts +110 -11
- package/dist/index.js +330 -32
- package/package.json +4 -4
- package/dist/chunk-KRDGCX7W.js +0 -27187
- package/dist/esm-PTFDM6PE.js +0 -8117
- package/dist/index.esm-SXKIFLA7.js +0 -158
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
7
|
|
|
8
8
|
// src/client.ts
|
|
9
9
|
import { privateKeyToAccount } from "viem/accounts";
|
|
@@ -116,9 +116,9 @@ async function createPaymentPayload(privateKey, fromAddress, recipient, amount,
|
|
|
116
116
|
return btoa(JSON.stringify(paymentData));
|
|
117
117
|
}
|
|
118
118
|
async function createSolanaPaymentPayload(secretKey, fromAddress, recipient, amount, feePayer, options = {}) {
|
|
119
|
-
const { Connection, PublicKey, TransactionMessage, VersionedTransaction, ComputeBudgetProgram } = await import("
|
|
120
|
-
const { getAssociatedTokenAddress, createTransferCheckedInstruction, getMint } = await import("
|
|
121
|
-
const { Keypair } = await import("
|
|
119
|
+
const { Connection, PublicKey, TransactionMessage, VersionedTransaction, ComputeBudgetProgram } = await import("@solana/web3.js");
|
|
120
|
+
const { getAssociatedTokenAddress, createTransferCheckedInstruction, getMint } = await import("@solana/spl-token");
|
|
121
|
+
const { Keypair } = await import("@solana/web3.js");
|
|
122
122
|
const rpcUrl = options.rpcUrl || "https://api.mainnet-beta.solana.com";
|
|
123
123
|
const connection = new Connection(rpcUrl);
|
|
124
124
|
const keypair = Keypair.fromSecretKey(secretKey);
|
|
@@ -223,6 +223,61 @@ function extractPaymentDetails(paymentRequired, preferredNetwork) {
|
|
|
223
223
|
|
|
224
224
|
// src/validation.ts
|
|
225
225
|
var LOCALHOST_DOMAINS = ["localhost", "127.0.0.1"];
|
|
226
|
+
var KNOWN_PROVIDERS = /* @__PURE__ */ new Set([
|
|
227
|
+
"openai",
|
|
228
|
+
"anthropic",
|
|
229
|
+
"google",
|
|
230
|
+
"deepseek",
|
|
231
|
+
"mistralai",
|
|
232
|
+
"meta-llama",
|
|
233
|
+
"together",
|
|
234
|
+
"xai",
|
|
235
|
+
"moonshot",
|
|
236
|
+
"nvidia",
|
|
237
|
+
"minimax",
|
|
238
|
+
"zai"
|
|
239
|
+
]);
|
|
240
|
+
function validateModel(model) {
|
|
241
|
+
if (!model || typeof model !== "string") {
|
|
242
|
+
throw new Error("Model must be a non-empty string");
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function validateMaxTokens(maxTokens) {
|
|
246
|
+
if (maxTokens === void 0 || maxTokens === null) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (typeof maxTokens !== "number" || !Number.isInteger(maxTokens)) {
|
|
250
|
+
throw new Error("maxTokens must be an integer");
|
|
251
|
+
}
|
|
252
|
+
if (maxTokens < 1) {
|
|
253
|
+
throw new Error("maxTokens must be positive (minimum: 1)");
|
|
254
|
+
}
|
|
255
|
+
if (maxTokens > 1e5) {
|
|
256
|
+
throw new Error("maxTokens too large (maximum: 100000)");
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
function validateTemperature(temperature) {
|
|
260
|
+
if (temperature === void 0 || temperature === null) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (typeof temperature !== "number") {
|
|
264
|
+
throw new Error("temperature must be a number");
|
|
265
|
+
}
|
|
266
|
+
if (temperature < 0 || temperature > 2) {
|
|
267
|
+
throw new Error("temperature must be between 0 and 2");
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function validateTopP(topP) {
|
|
271
|
+
if (topP === void 0 || topP === null) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (typeof topP !== "number") {
|
|
275
|
+
throw new Error("topP must be a number");
|
|
276
|
+
}
|
|
277
|
+
if (topP < 0 || topP > 1) {
|
|
278
|
+
throw new Error("topP must be between 0 and 1");
|
|
279
|
+
}
|
|
280
|
+
}
|
|
226
281
|
function validatePrivateKey(key) {
|
|
227
282
|
if (typeof key !== "string") {
|
|
228
283
|
throw new Error("Private key must be a string");
|
|
@@ -298,7 +353,7 @@ var DEFAULT_API_URL = "https://blockrun.ai/api";
|
|
|
298
353
|
var TESTNET_API_URL = "https://testnet.blockrun.ai/api";
|
|
299
354
|
var DEFAULT_MAX_TOKENS = 1024;
|
|
300
355
|
var DEFAULT_TIMEOUT = 6e4;
|
|
301
|
-
var SDK_VERSION = "
|
|
356
|
+
var SDK_VERSION = "1.5.0";
|
|
302
357
|
var USER_AGENT = `blockrun-ts/${SDK_VERSION}`;
|
|
303
358
|
var LLMClient = class {
|
|
304
359
|
static DEFAULT_API_URL = DEFAULT_API_URL;
|
|
@@ -335,13 +390,13 @@ var LLMClient = class {
|
|
|
335
390
|
/**
|
|
336
391
|
* Simple 1-line chat interface.
|
|
337
392
|
*
|
|
338
|
-
* @param model - Model ID (e.g., 'openai/gpt-
|
|
393
|
+
* @param model - Model ID (e.g., 'openai/gpt-5.2', 'anthropic/claude-sonnet-4.6')
|
|
339
394
|
* @param prompt - User message
|
|
340
395
|
* @param options - Optional chat parameters
|
|
341
396
|
* @returns Assistant's response text
|
|
342
397
|
*
|
|
343
398
|
* @example
|
|
344
|
-
* const response = await client.chat('gpt-
|
|
399
|
+
* const response = await client.chat('gpt-5.2', 'What is the capital of France?');
|
|
345
400
|
* console.log(response); // 'The capital of France is Paris.'
|
|
346
401
|
*/
|
|
347
402
|
async chat(model, prompt, options) {
|
|
@@ -487,6 +542,27 @@ var LLMClient = class {
|
|
|
487
542
|
headers: { "Content-Type": "application/json", "User-Agent": USER_AGENT },
|
|
488
543
|
body: JSON.stringify(body)
|
|
489
544
|
});
|
|
545
|
+
if (response.status === 502 || response.status === 503) {
|
|
546
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
547
|
+
const retryResp = await this.fetchWithTimeout(url, {
|
|
548
|
+
method: "POST",
|
|
549
|
+
headers: { "Content-Type": "application/json", "User-Agent": USER_AGENT },
|
|
550
|
+
body: JSON.stringify(body)
|
|
551
|
+
});
|
|
552
|
+
if (retryResp.status !== 502 && retryResp.status !== 503) {
|
|
553
|
+
if (retryResp.status === 402) return this.handlePaymentAndRetry(url, body, retryResp);
|
|
554
|
+
if (!retryResp.ok) {
|
|
555
|
+
let errorBody;
|
|
556
|
+
try {
|
|
557
|
+
errorBody = await retryResp.json();
|
|
558
|
+
} catch {
|
|
559
|
+
errorBody = { error: "Request failed" };
|
|
560
|
+
}
|
|
561
|
+
throw new APIError(`API error: ${retryResp.status}`, retryResp.status, sanitizeErrorResponse(errorBody));
|
|
562
|
+
}
|
|
563
|
+
return retryResp.json();
|
|
564
|
+
}
|
|
565
|
+
}
|
|
490
566
|
if (response.status === 402) {
|
|
491
567
|
return this.handlePaymentAndRetry(url, body, response);
|
|
492
568
|
}
|
|
@@ -552,6 +628,34 @@ var LLMClient = class {
|
|
|
552
628
|
},
|
|
553
629
|
body: JSON.stringify(body)
|
|
554
630
|
});
|
|
631
|
+
if (retryResponse.status === 502 || retryResponse.status === 503) {
|
|
632
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
633
|
+
const retryResp2 = await this.fetchWithTimeout(url, {
|
|
634
|
+
method: "POST",
|
|
635
|
+
headers: {
|
|
636
|
+
"Content-Type": "application/json",
|
|
637
|
+
"User-Agent": USER_AGENT,
|
|
638
|
+
"PAYMENT-SIGNATURE": paymentPayload
|
|
639
|
+
},
|
|
640
|
+
body: JSON.stringify(body)
|
|
641
|
+
});
|
|
642
|
+
if (retryResp2.status !== 502 && retryResp2.status !== 503) {
|
|
643
|
+
if (retryResp2.status === 402) throw new PaymentError("Payment was rejected. Check your wallet balance.");
|
|
644
|
+
if (!retryResp2.ok) {
|
|
645
|
+
let errorBody;
|
|
646
|
+
try {
|
|
647
|
+
errorBody = await retryResp2.json();
|
|
648
|
+
} catch {
|
|
649
|
+
errorBody = { error: "Request failed" };
|
|
650
|
+
}
|
|
651
|
+
throw new APIError(`API error after payment: ${retryResp2.status}`, retryResp2.status, sanitizeErrorResponse(errorBody));
|
|
652
|
+
}
|
|
653
|
+
const costUsd2 = parseFloat(details.amount) / 1e6;
|
|
654
|
+
this.sessionCalls += 1;
|
|
655
|
+
this.sessionTotalUsd += costUsd2;
|
|
656
|
+
return retryResp2.json();
|
|
657
|
+
}
|
|
658
|
+
}
|
|
555
659
|
if (retryResponse.status === 402) {
|
|
556
660
|
throw new PaymentError("Payment was rejected. Check your wallet balance.");
|
|
557
661
|
}
|
|
@@ -584,6 +688,27 @@ var LLMClient = class {
|
|
|
584
688
|
headers: { "Content-Type": "application/json", "User-Agent": USER_AGENT },
|
|
585
689
|
body: JSON.stringify(body)
|
|
586
690
|
});
|
|
691
|
+
if (response.status === 502 || response.status === 503) {
|
|
692
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
693
|
+
const retryResp = await this.fetchWithTimeout(url, {
|
|
694
|
+
method: "POST",
|
|
695
|
+
headers: { "Content-Type": "application/json", "User-Agent": USER_AGENT },
|
|
696
|
+
body: JSON.stringify(body)
|
|
697
|
+
});
|
|
698
|
+
if (retryResp.status !== 502 && retryResp.status !== 503) {
|
|
699
|
+
if (retryResp.status === 402) return this.handlePaymentAndRetryRaw(url, body, retryResp);
|
|
700
|
+
if (!retryResp.ok) {
|
|
701
|
+
let errorBody;
|
|
702
|
+
try {
|
|
703
|
+
errorBody = await retryResp.json();
|
|
704
|
+
} catch {
|
|
705
|
+
errorBody = { error: "Request failed" };
|
|
706
|
+
}
|
|
707
|
+
throw new APIError(`API error: ${retryResp.status}`, retryResp.status, sanitizeErrorResponse(errorBody));
|
|
708
|
+
}
|
|
709
|
+
return retryResp.json();
|
|
710
|
+
}
|
|
711
|
+
}
|
|
587
712
|
if (response.status === 402) {
|
|
588
713
|
return this.handlePaymentAndRetryRaw(url, body, response);
|
|
589
714
|
}
|
|
@@ -649,6 +774,34 @@ var LLMClient = class {
|
|
|
649
774
|
},
|
|
650
775
|
body: JSON.stringify(body)
|
|
651
776
|
});
|
|
777
|
+
if (retryResponse.status === 502 || retryResponse.status === 503) {
|
|
778
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
779
|
+
const retryResp2 = await this.fetchWithTimeout(url, {
|
|
780
|
+
method: "POST",
|
|
781
|
+
headers: {
|
|
782
|
+
"Content-Type": "application/json",
|
|
783
|
+
"User-Agent": USER_AGENT,
|
|
784
|
+
"PAYMENT-SIGNATURE": paymentPayload
|
|
785
|
+
},
|
|
786
|
+
body: JSON.stringify(body)
|
|
787
|
+
});
|
|
788
|
+
if (retryResp2.status !== 502 && retryResp2.status !== 503) {
|
|
789
|
+
if (retryResp2.status === 402) throw new PaymentError("Payment was rejected. Check your wallet balance.");
|
|
790
|
+
if (!retryResp2.ok) {
|
|
791
|
+
let errorBody;
|
|
792
|
+
try {
|
|
793
|
+
errorBody = await retryResp2.json();
|
|
794
|
+
} catch {
|
|
795
|
+
errorBody = { error: "Request failed" };
|
|
796
|
+
}
|
|
797
|
+
throw new APIError(`API error after payment: ${retryResp2.status}`, retryResp2.status, sanitizeErrorResponse(errorBody));
|
|
798
|
+
}
|
|
799
|
+
const costUsd2 = parseFloat(details.amount) / 1e6;
|
|
800
|
+
this.sessionCalls += 1;
|
|
801
|
+
this.sessionTotalUsd += costUsd2;
|
|
802
|
+
return retryResp2.json();
|
|
803
|
+
}
|
|
804
|
+
}
|
|
652
805
|
if (retryResponse.status === 402) {
|
|
653
806
|
throw new PaymentError("Payment was rejected. Check your wallet balance.");
|
|
654
807
|
}
|
|
@@ -681,6 +834,26 @@ var LLMClient = class {
|
|
|
681
834
|
method: "GET",
|
|
682
835
|
headers: { "User-Agent": USER_AGENT }
|
|
683
836
|
});
|
|
837
|
+
if (response.status === 502 || response.status === 503) {
|
|
838
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
839
|
+
const retryResp = await this.fetchWithTimeout(url, {
|
|
840
|
+
method: "GET",
|
|
841
|
+
headers: { "User-Agent": USER_AGENT }
|
|
842
|
+
});
|
|
843
|
+
if (retryResp.status !== 502 && retryResp.status !== 503) {
|
|
844
|
+
if (retryResp.status === 402) return this.handleGetPaymentAndRetryRaw(url, endpoint, params, retryResp);
|
|
845
|
+
if (!retryResp.ok) {
|
|
846
|
+
let errorBody;
|
|
847
|
+
try {
|
|
848
|
+
errorBody = await retryResp.json();
|
|
849
|
+
} catch {
|
|
850
|
+
errorBody = { error: "Request failed" };
|
|
851
|
+
}
|
|
852
|
+
throw new APIError(`API error: ${retryResp.status}`, retryResp.status, sanitizeErrorResponse(errorBody));
|
|
853
|
+
}
|
|
854
|
+
return retryResp.json();
|
|
855
|
+
}
|
|
856
|
+
}
|
|
684
857
|
if (response.status === 402) {
|
|
685
858
|
return this.handleGetPaymentAndRetryRaw(url, endpoint, params, response);
|
|
686
859
|
}
|
|
@@ -746,6 +919,32 @@ var LLMClient = class {
|
|
|
746
919
|
"PAYMENT-SIGNATURE": paymentPayload
|
|
747
920
|
}
|
|
748
921
|
});
|
|
922
|
+
if (retryResponse.status === 502 || retryResponse.status === 503) {
|
|
923
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
924
|
+
const retryResp2 = await this.fetchWithTimeout(retryUrl, {
|
|
925
|
+
method: "GET",
|
|
926
|
+
headers: {
|
|
927
|
+
"User-Agent": USER_AGENT,
|
|
928
|
+
"PAYMENT-SIGNATURE": paymentPayload
|
|
929
|
+
}
|
|
930
|
+
});
|
|
931
|
+
if (retryResp2.status !== 502 && retryResp2.status !== 503) {
|
|
932
|
+
if (retryResp2.status === 402) throw new PaymentError("Payment was rejected. Check your wallet balance.");
|
|
933
|
+
if (!retryResp2.ok) {
|
|
934
|
+
let errorBody;
|
|
935
|
+
try {
|
|
936
|
+
errorBody = await retryResp2.json();
|
|
937
|
+
} catch {
|
|
938
|
+
errorBody = { error: "Request failed" };
|
|
939
|
+
}
|
|
940
|
+
throw new APIError(`API error after payment: ${retryResp2.status}`, retryResp2.status, sanitizeErrorResponse(errorBody));
|
|
941
|
+
}
|
|
942
|
+
const costUsd2 = parseFloat(details.amount) / 1e6;
|
|
943
|
+
this.sessionCalls += 1;
|
|
944
|
+
this.sessionTotalUsd += costUsd2;
|
|
945
|
+
return retryResp2.json();
|
|
946
|
+
}
|
|
947
|
+
}
|
|
749
948
|
if (retryResponse.status === 402) {
|
|
750
949
|
throw new PaymentError("Payment was rejected. Check your wallet balance.");
|
|
751
950
|
}
|
|
@@ -804,7 +1003,18 @@ var LLMClient = class {
|
|
|
804
1003
|
);
|
|
805
1004
|
}
|
|
806
1005
|
const data = await response.json();
|
|
807
|
-
return data.data || []
|
|
1006
|
+
return (data.data || []).map((m) => ({
|
|
1007
|
+
id: m.id,
|
|
1008
|
+
name: m.name || m.id,
|
|
1009
|
+
provider: m.owned_by || "",
|
|
1010
|
+
description: m.description || "",
|
|
1011
|
+
inputPrice: m.pricing?.input ?? m.pricing?.flat ?? 0,
|
|
1012
|
+
outputPrice: m.pricing?.output ?? 0,
|
|
1013
|
+
contextWindow: m.context_window || 0,
|
|
1014
|
+
maxOutput: m.max_output || 0,
|
|
1015
|
+
categories: m.categories || [],
|
|
1016
|
+
available: true
|
|
1017
|
+
}));
|
|
808
1018
|
}
|
|
809
1019
|
/**
|
|
810
1020
|
* List available image generation models with pricing.
|
|
@@ -1581,7 +1791,7 @@ import * as os2 from "os";
|
|
|
1581
1791
|
var WALLET_DIR2 = path2.join(os2.homedir(), ".blockrun");
|
|
1582
1792
|
var SOLANA_WALLET_FILE = path2.join(WALLET_DIR2, ".solana-session");
|
|
1583
1793
|
function createSolanaWallet() {
|
|
1584
|
-
const { Keypair } = (
|
|
1794
|
+
const { Keypair } = __require("@solana/web3.js");
|
|
1585
1795
|
const bs58 = __require("bs58");
|
|
1586
1796
|
const keypair = Keypair.generate();
|
|
1587
1797
|
return {
|
|
@@ -1603,7 +1813,7 @@ async function solanaKeyToBytes(privateKey) {
|
|
|
1603
1813
|
}
|
|
1604
1814
|
}
|
|
1605
1815
|
async function solanaPublicKey(privateKey) {
|
|
1606
|
-
const { Keypair } = await import("
|
|
1816
|
+
const { Keypair } = await import("@solana/web3.js");
|
|
1607
1817
|
const bytes = await solanaKeyToBytes(privateKey);
|
|
1608
1818
|
return Keypair.fromSecretKey(bytes).publicKey.toBase58();
|
|
1609
1819
|
}
|
|
@@ -2203,6 +2413,8 @@ import * as path3 from "path";
|
|
|
2203
2413
|
import * as os3 from "os";
|
|
2204
2414
|
import * as crypto2 from "crypto";
|
|
2205
2415
|
var CACHE_DIR = path3.join(os3.homedir(), ".blockrun", "cache");
|
|
2416
|
+
var DATA_DIR = path3.join(os3.homedir(), ".blockrun", "data");
|
|
2417
|
+
var COST_LOG_FILE = path3.join(os3.homedir(), ".blockrun", "cost_log.jsonl");
|
|
2206
2418
|
var DEFAULT_TTL = {
|
|
2207
2419
|
"/v1/x/": 3600 * 1e3,
|
|
2208
2420
|
"/v1/partner/": 3600 * 1e3,
|
|
@@ -2267,26 +2479,80 @@ function setCache(key, data, ttlMs) {
|
|
|
2267
2479
|
} catch {
|
|
2268
2480
|
}
|
|
2269
2481
|
}
|
|
2270
|
-
function
|
|
2271
|
-
const
|
|
2272
|
-
|
|
2482
|
+
function readableFilename(endpoint, body) {
|
|
2483
|
+
const now = /* @__PURE__ */ new Date();
|
|
2484
|
+
const ts = now.toISOString().slice(0, 10) + "_" + String(now.getHours()).padStart(2, "0") + String(now.getMinutes()).padStart(2, "0") + String(now.getSeconds()).padStart(2, "0");
|
|
2485
|
+
let ep = endpoint.replace(/\/+$/, "").split("/").pop() || "";
|
|
2486
|
+
if (endpoint.includes("/v1/chat/")) {
|
|
2487
|
+
ep = "chat";
|
|
2488
|
+
} else if (endpoint.includes("/v1/x/")) {
|
|
2489
|
+
ep = "x_" + ep;
|
|
2490
|
+
} else if (endpoint.includes("/v1/search")) {
|
|
2491
|
+
ep = "search";
|
|
2492
|
+
} else if (endpoint.includes("/v1/image")) {
|
|
2493
|
+
ep = "image";
|
|
2494
|
+
}
|
|
2495
|
+
let label = body.query || body.username || body.handle || body.model || (typeof body.prompt === "string" ? body.prompt.slice(0, 40) : "") || "";
|
|
2496
|
+
label = String(label).replace(/[^a-zA-Z0-9_\-]/g, "_").slice(0, 40).replace(/^_+|_+$/g, "");
|
|
2497
|
+
return label ? `${ep}_${ts}_${label}.json` : `${ep}_${ts}.json`;
|
|
2498
|
+
}
|
|
2499
|
+
function saveReadable(endpoint, body, response, costUsd) {
|
|
2273
2500
|
try {
|
|
2274
|
-
fs3.mkdirSync(
|
|
2501
|
+
fs3.mkdirSync(DATA_DIR, { recursive: true });
|
|
2275
2502
|
} catch {
|
|
2276
2503
|
}
|
|
2277
|
-
const
|
|
2504
|
+
const filename = readableFilename(endpoint, body);
|
|
2278
2505
|
const entry = {
|
|
2279
|
-
|
|
2506
|
+
saved_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2280
2507
|
endpoint,
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2508
|
+
cost_usd: costUsd,
|
|
2509
|
+
request: body,
|
|
2510
|
+
response
|
|
2284
2511
|
};
|
|
2285
2512
|
try {
|
|
2286
|
-
fs3.writeFileSync(
|
|
2513
|
+
fs3.writeFileSync(path3.join(DATA_DIR, filename), JSON.stringify(entry, null, 2));
|
|
2514
|
+
} catch {
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
function appendCostLog(endpoint, costUsd) {
|
|
2518
|
+
if (costUsd <= 0) return;
|
|
2519
|
+
try {
|
|
2520
|
+
fs3.mkdirSync(path3.dirname(COST_LOG_FILE), { recursive: true });
|
|
2521
|
+
} catch {
|
|
2522
|
+
}
|
|
2523
|
+
const entry = {
|
|
2524
|
+
ts: Date.now() / 1e3,
|
|
2525
|
+
endpoint,
|
|
2526
|
+
cost_usd: costUsd
|
|
2527
|
+
};
|
|
2528
|
+
try {
|
|
2529
|
+
fs3.appendFileSync(COST_LOG_FILE, JSON.stringify(entry) + "\n");
|
|
2287
2530
|
} catch {
|
|
2288
2531
|
}
|
|
2289
2532
|
}
|
|
2533
|
+
function saveToCache(endpoint, body, response, costUsd = 0) {
|
|
2534
|
+
const ttl = getTtl(endpoint);
|
|
2535
|
+
if (ttl > 0) {
|
|
2536
|
+
try {
|
|
2537
|
+
fs3.mkdirSync(CACHE_DIR, { recursive: true });
|
|
2538
|
+
} catch {
|
|
2539
|
+
}
|
|
2540
|
+
const key = cacheKey(endpoint, body);
|
|
2541
|
+
const entry = {
|
|
2542
|
+
cachedAt: Date.now(),
|
|
2543
|
+
endpoint,
|
|
2544
|
+
body,
|
|
2545
|
+
response,
|
|
2546
|
+
costUsd
|
|
2547
|
+
};
|
|
2548
|
+
try {
|
|
2549
|
+
fs3.writeFileSync(cachePath(key), JSON.stringify(entry));
|
|
2550
|
+
} catch {
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
saveReadable(endpoint, body, response, costUsd);
|
|
2554
|
+
appendCostLog(endpoint, costUsd);
|
|
2555
|
+
}
|
|
2290
2556
|
function clearCache() {
|
|
2291
2557
|
if (!fs3.existsSync(CACHE_DIR)) return 0;
|
|
2292
2558
|
let count = 0;
|
|
@@ -2305,6 +2571,32 @@ function clearCache() {
|
|
|
2305
2571
|
}
|
|
2306
2572
|
return count;
|
|
2307
2573
|
}
|
|
2574
|
+
function getCostLogSummary() {
|
|
2575
|
+
if (!fs3.existsSync(COST_LOG_FILE)) {
|
|
2576
|
+
return { totalUsd: 0, calls: 0, byEndpoint: {} };
|
|
2577
|
+
}
|
|
2578
|
+
let totalUsd = 0;
|
|
2579
|
+
let calls = 0;
|
|
2580
|
+
const byEndpoint = {};
|
|
2581
|
+
try {
|
|
2582
|
+
const content = fs3.readFileSync(COST_LOG_FILE, "utf-8").trim();
|
|
2583
|
+
if (!content) return { totalUsd: 0, calls: 0, byEndpoint: {} };
|
|
2584
|
+
for (const line of content.split("\n")) {
|
|
2585
|
+
if (!line) continue;
|
|
2586
|
+
try {
|
|
2587
|
+
const entry = JSON.parse(line);
|
|
2588
|
+
const cost = entry.cost_usd ?? 0;
|
|
2589
|
+
const ep = entry.endpoint ?? "unknown";
|
|
2590
|
+
totalUsd += cost;
|
|
2591
|
+
calls += 1;
|
|
2592
|
+
byEndpoint[ep] = (byEndpoint[ep] || 0) + cost;
|
|
2593
|
+
} catch {
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
} catch {
|
|
2597
|
+
}
|
|
2598
|
+
return { totalUsd, calls, byEndpoint };
|
|
2599
|
+
}
|
|
2308
2600
|
|
|
2309
2601
|
// src/setup.ts
|
|
2310
2602
|
function setupAgentWallet(options) {
|
|
@@ -2346,27 +2638,27 @@ async function status() {
|
|
|
2346
2638
|
import * as fs4 from "fs";
|
|
2347
2639
|
import * as path4 from "path";
|
|
2348
2640
|
import * as os4 from "os";
|
|
2349
|
-
var
|
|
2350
|
-
var
|
|
2641
|
+
var DATA_DIR2 = path4.join(os4.homedir(), ".blockrun", "data");
|
|
2642
|
+
var COST_LOG_FILE2 = path4.join(DATA_DIR2, "costs.jsonl");
|
|
2351
2643
|
function logCost(entry) {
|
|
2352
2644
|
try {
|
|
2353
|
-
fs4.mkdirSync(
|
|
2645
|
+
fs4.mkdirSync(DATA_DIR2, { recursive: true });
|
|
2354
2646
|
} catch {
|
|
2355
2647
|
}
|
|
2356
2648
|
try {
|
|
2357
|
-
fs4.appendFileSync(
|
|
2649
|
+
fs4.appendFileSync(COST_LOG_FILE2, JSON.stringify(entry) + "\n");
|
|
2358
2650
|
} catch {
|
|
2359
2651
|
}
|
|
2360
2652
|
}
|
|
2361
2653
|
function getCostSummary() {
|
|
2362
|
-
if (!fs4.existsSync(
|
|
2654
|
+
if (!fs4.existsSync(COST_LOG_FILE2)) {
|
|
2363
2655
|
return { totalUsd: 0, calls: 0, byModel: {} };
|
|
2364
2656
|
}
|
|
2365
2657
|
let totalUsd = 0;
|
|
2366
2658
|
let calls = 0;
|
|
2367
2659
|
const byModel = {};
|
|
2368
2660
|
try {
|
|
2369
|
-
const content = fs4.readFileSync(
|
|
2661
|
+
const content = fs4.readFileSync(COST_LOG_FILE2, "utf-8").trim();
|
|
2370
2662
|
if (!content) return { totalUsd: 0, calls: 0, byModel: {} };
|
|
2371
2663
|
for (const line of content.split("\n")) {
|
|
2372
2664
|
if (!line) continue;
|
|
@@ -2667,6 +2959,7 @@ export {
|
|
|
2667
2959
|
BASE_CHAIN_ID,
|
|
2668
2960
|
BlockrunError,
|
|
2669
2961
|
ImageClient,
|
|
2962
|
+
KNOWN_PROVIDERS,
|
|
2670
2963
|
LLMClient,
|
|
2671
2964
|
OpenAI,
|
|
2672
2965
|
PaymentError,
|
|
@@ -2690,6 +2983,7 @@ export {
|
|
|
2690
2983
|
formatWalletCreatedMessage,
|
|
2691
2984
|
getCached,
|
|
2692
2985
|
getCachedByRequest,
|
|
2986
|
+
getCostLogSummary,
|
|
2693
2987
|
getCostSummary,
|
|
2694
2988
|
getEip681Uri,
|
|
2695
2989
|
getOrCreateSolanaWallet,
|
|
@@ -2712,5 +3006,9 @@ export {
|
|
|
2712
3006
|
solanaKeyToBytes,
|
|
2713
3007
|
solanaPublicKey,
|
|
2714
3008
|
status,
|
|
2715
|
-
testnetClient
|
|
3009
|
+
testnetClient,
|
|
3010
|
+
validateMaxTokens,
|
|
3011
|
+
validateModel,
|
|
3012
|
+
validateTemperature,
|
|
3013
|
+
validateTopP
|
|
2716
3014
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blockrun/llm",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "BlockRun LLM Gateway SDK - Pay-per-request AI via x402 on Base and Solana",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"README.md"
|
|
19
19
|
],
|
|
20
20
|
"scripts": {
|
|
21
|
-
"build": "tsup src/index.ts --format cjs,esm --dts --clean --external @anthropic-ai/sdk",
|
|
22
|
-
"dev": "tsup src/index.ts --format cjs,esm --dts --watch --external @anthropic-ai/sdk",
|
|
21
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean --external @anthropic-ai/sdk --external @solana/web3.js --external @solana/spl-token --external bs58",
|
|
22
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch --external @anthropic-ai/sdk --external @solana/web3.js --external @solana/spl-token --external bs58",
|
|
23
23
|
"test": "vitest",
|
|
24
24
|
"lint": "eslint src/",
|
|
25
25
|
"typecheck": "tsc --noEmit"
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"url": "https://github.com/BlockRunAI/blockrun-llm-ts/issues"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@blockrun/clawrouter": "^0.
|
|
51
|
+
"@blockrun/clawrouter": "^0.12.71",
|
|
52
52
|
"bs58": "^6.0.0",
|
|
53
53
|
"viem": "^2.21.0"
|
|
54
54
|
},
|