@blockrun/llm 0.1.1 → 0.2.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/package.json CHANGED
@@ -1,15 +1,16 @@
1
1
  {
2
2
  "name": "@blockrun/llm",
3
- "version": "0.1.1",
4
- "description": "BlockRun LLM Gateway SDK - Pay-per-request AI via x402 on Base and Solana",
3
+ "version": "0.2.0",
4
+ "type": "module",
5
+ "description": "BlockRun LLM Gateway SDK - Pay-per-request AI via x402 on Base",
5
6
  "main": "dist/index.js",
6
7
  "module": "dist/index.mjs",
7
8
  "types": "dist/index.d.ts",
8
9
  "exports": {
9
10
  ".": {
11
+ "types": "./dist/index.d.ts",
10
12
  "import": "./dist/index.mjs",
11
- "require": "./dist/index.js",
12
- "types": "./dist/index.d.ts"
13
+ "require": "./dist/index.js"
13
14
  }
14
15
  },
15
16
  "files": [
@@ -54,12 +55,16 @@
54
55
  "@solana/spl-token": "^0.4.0"
55
56
  },
56
57
  "devDependencies": {
58
+ "@eslint/js": "^9.0.0",
57
59
  "@types/node": "^20.0.0",
60
+ "eslint": "^9.0.0",
58
61
  "tsup": "^8.0.0",
59
62
  "typescript": "^5.0.0",
63
+ "typescript-eslint": "^8.0.0",
60
64
  "vitest": "^1.0.0"
61
65
  },
62
66
  "engines": {
63
- "node": ">=18"
64
- }
67
+ "node": ">=20"
68
+ },
69
+ "packageManager": "pnpm@9.15.4"
65
70
  }
@@ -1,90 +0,0 @@
1
- // src/validation.ts
2
- var LOCALHOST_DOMAINS = ["localhost", "127.0.0.1"];
3
- function validatePrivateKey(key) {
4
- if (typeof key !== "string") {
5
- throw new Error("Private key must be a string");
6
- }
7
- if (!key.startsWith("0x")) {
8
- throw new Error("Private key must start with 0x");
9
- }
10
- if (key.length !== 66) {
11
- throw new Error(
12
- "Private key must be 66 characters (0x + 64 hexadecimal characters)"
13
- );
14
- }
15
- if (!/^0x[0-9a-fA-F]{64}$/.test(key)) {
16
- throw new Error(
17
- "Private key must contain only hexadecimal characters (0-9, a-f, A-F)"
18
- );
19
- }
20
- }
21
- function validateApiUrl(url) {
22
- let parsed;
23
- try {
24
- parsed = new URL(url);
25
- } catch (error) {
26
- throw new Error("Invalid API URL format");
27
- }
28
- const isLocalhost = LOCALHOST_DOMAINS.includes(parsed.hostname);
29
- if (parsed.protocol !== "https:" && !isLocalhost) {
30
- throw new Error(
31
- `API URL must use HTTPS for non-localhost endpoints. Use https:// instead of ${parsed.protocol}//`
32
- );
33
- }
34
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
35
- throw new Error(
36
- `Invalid protocol: ${parsed.protocol}. Use http:// or https://`
37
- );
38
- }
39
- }
40
- function sanitizeErrorResponse(errorBody) {
41
- if (typeof errorBody !== "object" || errorBody === null) {
42
- return { message: "API request failed" };
43
- }
44
- const body = errorBody;
45
- return {
46
- message: typeof body.error === "string" ? body.error : "API request failed",
47
- code: typeof body.code === "string" ? body.code : void 0
48
- };
49
- }
50
- function validateResourceUrl(url, baseUrl) {
51
- try {
52
- const parsed = new URL(url);
53
- const baseParsed = new URL(baseUrl);
54
- if (parsed.hostname !== baseParsed.hostname) {
55
- console.warn(
56
- `Resource URL hostname mismatch: ${parsed.hostname} vs ${baseParsed.hostname}. Using safe default instead.`
57
- );
58
- return `${baseUrl}/v1/chat/completions`;
59
- }
60
- if (parsed.protocol !== baseParsed.protocol) {
61
- console.warn(
62
- `Resource URL protocol mismatch: ${parsed.protocol} vs ${baseParsed.protocol}. Using safe default instead.`
63
- );
64
- return `${baseUrl}/v1/chat/completions`;
65
- }
66
- return url;
67
- } catch (error) {
68
- console.warn(`Invalid resource URL format: ${url}. Using safe default.`);
69
- return `${baseUrl}/v1/chat/completions`;
70
- }
71
- }
72
- function extractPrivateKey(account) {
73
- if ("source" in account && typeof account.source === "string") {
74
- return account.source;
75
- }
76
- if ("key" in account && typeof account.key === "string") {
77
- return account.key;
78
- }
79
- throw new Error(
80
- "Unable to extract private key from account. This may indicate an incompatible viem version."
81
- );
82
- }
83
-
84
- export {
85
- validatePrivateKey,
86
- validateApiUrl,
87
- sanitizeErrorResponse,
88
- validateResourceUrl,
89
- extractPrivateKey
90
- };
@@ -1,206 +0,0 @@
1
- // src/x402.ts
2
- import { signTypedData } from "viem/accounts";
3
- var BASE_CHAIN_ID = 8453;
4
- var USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
5
- var SOLANA_NETWORK = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
6
- var USDC_SOLANA = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
7
- var SOLANA_USDC_DECIMALS = 6;
8
- var DEFAULT_COMPUTE_UNIT_PRICE_MICROLAMPORTS = 1;
9
- var DEFAULT_COMPUTE_UNIT_LIMIT = 8e3;
10
- var USDC_DOMAIN = {
11
- name: "USD Coin",
12
- version: "2",
13
- chainId: BASE_CHAIN_ID,
14
- verifyingContract: USDC_BASE
15
- };
16
- var TRANSFER_TYPES = {
17
- TransferWithAuthorization: [
18
- { name: "from", type: "address" },
19
- { name: "to", type: "address" },
20
- { name: "value", type: "uint256" },
21
- { name: "validAfter", type: "uint256" },
22
- { name: "validBefore", type: "uint256" },
23
- { name: "nonce", type: "bytes32" }
24
- ]
25
- };
26
- function createNonce() {
27
- const bytes = new Uint8Array(32);
28
- crypto.getRandomValues(bytes);
29
- return `0x${Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("")}`;
30
- }
31
- async function createPaymentPayload(privateKey, fromAddress, recipient, amount, network = "eip155:8453", options = {}) {
32
- const now = Math.floor(Date.now() / 1e3);
33
- const validAfter = now - 600;
34
- const validBefore = now + (options.maxTimeoutSeconds || 300);
35
- const nonce = createNonce();
36
- const domain = USDC_DOMAIN;
37
- const signature = await signTypedData({
38
- privateKey,
39
- domain,
40
- types: TRANSFER_TYPES,
41
- primaryType: "TransferWithAuthorization",
42
- message: {
43
- from: fromAddress,
44
- to: recipient,
45
- value: BigInt(amount),
46
- validAfter: BigInt(validAfter),
47
- validBefore: BigInt(validBefore),
48
- nonce
49
- }
50
- });
51
- const paymentData = {
52
- x402Version: 2,
53
- resource: {
54
- url: options.resourceUrl || "https://blockrun.ai/api/v1/chat/completions",
55
- description: options.resourceDescription || "BlockRun AI API call",
56
- mimeType: "application/json"
57
- },
58
- accepted: {
59
- scheme: "exact",
60
- network,
61
- amount,
62
- asset: USDC_BASE,
63
- payTo: recipient,
64
- maxTimeoutSeconds: options.maxTimeoutSeconds || 300,
65
- extra: { name: "USD Coin", version: "2" }
66
- },
67
- payload: {
68
- signature,
69
- authorization: {
70
- from: fromAddress,
71
- to: recipient,
72
- value: amount,
73
- validAfter: validAfter.toString(),
74
- validBefore: validBefore.toString(),
75
- nonce
76
- }
77
- },
78
- extensions: options.extensions || {}
79
- };
80
- return btoa(JSON.stringify(paymentData));
81
- }
82
- async function createSolanaPaymentPayload(secretKey, fromAddress, recipient, amount, feePayer, options = {}) {
83
- const { Connection, PublicKey, TransactionMessage, VersionedTransaction, ComputeBudgetProgram } = await import("./index.esm-FLTVMT4C.mjs");
84
- const { getAssociatedTokenAddress, createTransferCheckedInstruction, getMint } = await import("./esm-3BUZROFC.mjs");
85
- const { Keypair } = await import("./index.esm-FLTVMT4C.mjs");
86
- const rpcUrl = options.rpcUrl || "https://api.mainnet-beta.solana.com";
87
- const connection = new Connection(rpcUrl);
88
- const keypair = Keypair.fromSecretKey(secretKey);
89
- const feePayerPubkey = new PublicKey(feePayer);
90
- const ownerPubkey = keypair.publicKey;
91
- const tokenMint = new PublicKey(USDC_SOLANA);
92
- const payToPubkey = new PublicKey(recipient);
93
- const mintInfo = await getMint(connection, tokenMint);
94
- const sourceATA = await getAssociatedTokenAddress(tokenMint, ownerPubkey, false);
95
- const destinationATA = await getAssociatedTokenAddress(tokenMint, payToPubkey, false);
96
- const { blockhash } = await connection.getLatestBlockhash();
97
- const setComputeUnitPriceIx = ComputeBudgetProgram.setComputeUnitPrice({
98
- microLamports: DEFAULT_COMPUTE_UNIT_PRICE_MICROLAMPORTS
99
- });
100
- const setComputeUnitLimitIx = ComputeBudgetProgram.setComputeUnitLimit({
101
- units: DEFAULT_COMPUTE_UNIT_LIMIT
102
- });
103
- const transferIx = createTransferCheckedInstruction(
104
- sourceATA,
105
- tokenMint,
106
- destinationATA,
107
- ownerPubkey,
108
- BigInt(amount),
109
- mintInfo.decimals
110
- );
111
- const messageV0 = new TransactionMessage({
112
- payerKey: feePayerPubkey,
113
- recentBlockhash: blockhash,
114
- instructions: [setComputeUnitLimitIx, setComputeUnitPriceIx, transferIx]
115
- }).compileToV0Message();
116
- const transaction = new VersionedTransaction(messageV0);
117
- transaction.sign([keypair]);
118
- const serializedTx = Buffer.from(transaction.serialize()).toString("base64");
119
- const paymentData = {
120
- x402Version: 2,
121
- resource: {
122
- url: options.resourceUrl || "https://blockrun.ai/api/v1/chat/completions",
123
- description: options.resourceDescription || "BlockRun AI API call",
124
- mimeType: "application/json"
125
- },
126
- accepted: {
127
- scheme: "exact",
128
- network: SOLANA_NETWORK,
129
- amount,
130
- asset: USDC_SOLANA,
131
- payTo: recipient,
132
- maxTimeoutSeconds: options.maxTimeoutSeconds || 300,
133
- extra: options.extra || { feePayer }
134
- },
135
- payload: {
136
- transaction: serializedTx
137
- },
138
- extensions: options.extensions || {}
139
- };
140
- return btoa(JSON.stringify(paymentData));
141
- }
142
- function parsePaymentRequired(headerValue) {
143
- try {
144
- const decoded = atob(headerValue);
145
- const parsed = JSON.parse(decoded);
146
- if (!parsed.accepts || !Array.isArray(parsed.accepts)) {
147
- throw new Error("Invalid payment required structure: missing or invalid 'accepts' field");
148
- }
149
- return parsed;
150
- } catch (error) {
151
- if (error instanceof Error) {
152
- if (error.message.includes("Invalid payment required structure")) {
153
- throw error;
154
- }
155
- throw new Error("Failed to parse payment required header: invalid format");
156
- }
157
- throw new Error("Failed to parse payment required header");
158
- }
159
- }
160
- function extractPaymentDetails(paymentRequired, preferredNetwork) {
161
- const accepts = paymentRequired.accepts || [];
162
- if (accepts.length === 0) {
163
- throw new Error("No payment options in payment required response");
164
- }
165
- let option = null;
166
- if (preferredNetwork) {
167
- option = accepts.find((opt) => opt.network === preferredNetwork) || null;
168
- }
169
- if (!option) {
170
- option = accepts[0];
171
- }
172
- const amount = option.amount || option.maxAmountRequired;
173
- if (!amount) {
174
- throw new Error("No amount found in payment requirements");
175
- }
176
- return {
177
- amount,
178
- recipient: option.payTo,
179
- network: option.network,
180
- asset: option.asset,
181
- scheme: option.scheme,
182
- maxTimeoutSeconds: option.maxTimeoutSeconds || 300,
183
- extra: option.extra,
184
- resource: paymentRequired.resource
185
- };
186
- }
187
- function isSolanaNetwork(network) {
188
- return network.startsWith("solana:");
189
- }
190
- function getAvailableNetworks(paymentRequired) {
191
- return paymentRequired.accepts.map((opt) => opt.network).filter((network) => Boolean(network));
192
- }
193
-
194
- export {
195
- BASE_CHAIN_ID,
196
- USDC_BASE,
197
- SOLANA_NETWORK,
198
- USDC_SOLANA,
199
- SOLANA_USDC_DECIMALS,
200
- createPaymentPayload,
201
- createSolanaPaymentPayload,
202
- parsePaymentRequired,
203
- extractPaymentDetails,
204
- isSolanaNetwork,
205
- getAvailableNetworks
206
- };
package/dist/index.d.mts DELETED
@@ -1,295 +0,0 @@
1
- /**
2
- * Type definitions for BlockRun LLM SDK
3
- */
4
- interface ChatMessage {
5
- role: "system" | "user" | "assistant";
6
- content: string;
7
- }
8
- interface ChatChoice {
9
- index: number;
10
- message: ChatMessage;
11
- finish_reason?: string;
12
- }
13
- interface ChatUsage {
14
- prompt_tokens: number;
15
- completion_tokens: number;
16
- total_tokens: number;
17
- }
18
- interface ChatResponse {
19
- id: string;
20
- object: string;
21
- created: number;
22
- model: string;
23
- choices: ChatChoice[];
24
- usage?: ChatUsage;
25
- }
26
- interface Model {
27
- id: string;
28
- name: string;
29
- provider: string;
30
- description: string;
31
- inputPrice: number;
32
- outputPrice: number;
33
- contextWindow: number;
34
- maxOutput: number;
35
- available: boolean;
36
- }
37
- interface LLMClientOptions {
38
- /** EVM wallet private key (hex string starting with 0x). Optional if BASE_CHAIN_WALLET_KEY env var is set. */
39
- privateKey?: `0x${string}` | string;
40
- /** API endpoint URL (default: https://blockrun.ai/api) */
41
- apiUrl?: string;
42
- /** Request timeout in milliseconds (default: 60000) */
43
- timeout?: number;
44
- }
45
- interface ChatOptions {
46
- /** System prompt */
47
- system?: string;
48
- /** Max tokens to generate */
49
- maxTokens?: number;
50
- /** Sampling temperature */
51
- temperature?: number;
52
- /** Nucleus sampling parameter */
53
- topP?: number;
54
- }
55
- interface ChatCompletionOptions {
56
- /** Max tokens to generate */
57
- maxTokens?: number;
58
- /** Sampling temperature */
59
- temperature?: number;
60
- /** Nucleus sampling parameter */
61
- topP?: number;
62
- }
63
- declare class BlockrunError extends Error {
64
- constructor(message: string);
65
- }
66
- declare class PaymentError extends BlockrunError {
67
- constructor(message: string);
68
- }
69
- declare class APIError extends BlockrunError {
70
- statusCode: number;
71
- response?: unknown;
72
- constructor(message: string, statusCode: number, response?: unknown);
73
- }
74
-
75
- /**
76
- * BlockRun LLM Client - Main SDK entry point.
77
- *
78
- * Usage:
79
- * import { LLMClient } from '@blockrun/llm';
80
- *
81
- * // Option 1: Use BASE_CHAIN_WALLET_KEY env var
82
- * const client = new LLMClient();
83
- *
84
- * // Option 2: Pass private key directly
85
- * const client = new LLMClient({ privateKey: '0x...' });
86
- *
87
- * const response = await client.chat('openai/gpt-4o', 'Hello!');
88
- * console.log(response);
89
- */
90
-
91
- /**
92
- * BlockRun LLM Gateway Client.
93
- *
94
- * Provides access to multiple LLM providers (OpenAI, Anthropic, Google, etc.)
95
- * with automatic x402 micropayments on Base chain.
96
- */
97
- declare class LLMClient {
98
- private account;
99
- private privateKey;
100
- private apiUrl;
101
- private timeout;
102
- /**
103
- * Initialize the BlockRun LLM client.
104
- *
105
- * @param options - Client configuration options (optional if BASE_CHAIN_WALLET_KEY env var is set)
106
- */
107
- constructor(options?: LLMClientOptions);
108
- /**
109
- * Simple 1-line chat interface.
110
- *
111
- * @param model - Model ID (e.g., 'openai/gpt-4o', 'anthropic/claude-sonnet-4')
112
- * @param prompt - User message
113
- * @param options - Optional chat parameters
114
- * @returns Assistant's response text
115
- *
116
- * @example
117
- * const response = await client.chat('gpt-4o', 'What is the capital of France?');
118
- * console.log(response); // 'The capital of France is Paris.'
119
- */
120
- chat(model: string, prompt: string, options?: ChatOptions): Promise<string>;
121
- /**
122
- * Full chat completion interface (OpenAI-compatible).
123
- *
124
- * @param model - Model ID
125
- * @param messages - Array of messages with role and content
126
- * @param options - Optional completion parameters
127
- * @returns ChatResponse object with choices and usage
128
- */
129
- chatCompletion(model: string, messages: ChatMessage[], options?: ChatCompletionOptions): Promise<ChatResponse>;
130
- /**
131
- * Make a request with automatic x402 payment handling.
132
- */
133
- private requestWithPayment;
134
- /**
135
- * Handle 402 response: parse requirements, sign payment, retry.
136
- */
137
- private handlePaymentAndRetry;
138
- /**
139
- * Fetch with timeout.
140
- */
141
- private fetchWithTimeout;
142
- /**
143
- * List available models with pricing.
144
- */
145
- listModels(): Promise<Model[]>;
146
- /**
147
- * Get the wallet address being used for payments.
148
- */
149
- getWalletAddress(): string;
150
- }
151
-
152
- /**
153
- * x402 Payment Protocol v2 Implementation for BlockRun.
154
- *
155
- * This module handles creating signed payment payloads for the x402 v2 protocol.
156
- * The private key is used ONLY for local signing and NEVER leaves the client.
157
- */
158
-
159
- declare const BASE_CHAIN_ID = 8453;
160
- declare const USDC_BASE: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
161
-
162
- /**
163
- * OpenAI-compatible API wrapper for BlockRun LLM SDK.
164
- *
165
- * Drop-in replacement for OpenAI SDK - just change the import and use walletKey instead of apiKey.
166
- *
167
- * @example
168
- * // Before (OpenAI)
169
- * import OpenAI from 'openai';
170
- * const client = new OpenAI({ apiKey: 'sk-...' });
171
- *
172
- * // After (BlockRun)
173
- * import { OpenAI } from '@blockrun/llm';
174
- * const client = new OpenAI({ walletKey: '0x...' });
175
- *
176
- * // Rest of your code stays exactly the same!
177
- * const response = await client.chat.completions.create({
178
- * model: 'gpt-4o',
179
- * messages: [{ role: 'user', content: 'Hello!' }]
180
- * });
181
- */
182
-
183
- interface OpenAIClientOptions {
184
- /** EVM wallet private key (replaces apiKey) */
185
- walletKey?: `0x${string}` | string;
186
- /** Alternative: use privateKey like LLMClient */
187
- privateKey?: `0x${string}` | string;
188
- /** API endpoint URL (default: https://blockrun.ai/api) */
189
- baseURL?: string;
190
- /** Request timeout in milliseconds */
191
- timeout?: number;
192
- }
193
- interface OpenAIChatCompletionParams {
194
- model: string;
195
- messages: Array<{
196
- role: "system" | "user" | "assistant";
197
- content: string;
198
- }>;
199
- max_tokens?: number;
200
- temperature?: number;
201
- top_p?: number;
202
- stream?: boolean;
203
- n?: number;
204
- stop?: string | string[];
205
- presence_penalty?: number;
206
- frequency_penalty?: number;
207
- user?: string;
208
- }
209
- interface OpenAIChatCompletionChoice {
210
- index: number;
211
- message: {
212
- role: "assistant";
213
- content: string;
214
- };
215
- finish_reason: string | null;
216
- }
217
- interface OpenAIChatCompletionResponse {
218
- id: string;
219
- object: "chat.completion";
220
- created: number;
221
- model: string;
222
- choices: OpenAIChatCompletionChoice[];
223
- usage?: {
224
- prompt_tokens: number;
225
- completion_tokens: number;
226
- total_tokens: number;
227
- };
228
- }
229
- interface OpenAIChatCompletionChunk {
230
- id: string;
231
- object: "chat.completion.chunk";
232
- created: number;
233
- model: string;
234
- choices: Array<{
235
- index: number;
236
- delta: {
237
- role?: "assistant";
238
- content?: string;
239
- };
240
- finish_reason: string | null;
241
- }>;
242
- }
243
- /**
244
- * Chat completions API (OpenAI-compatible)
245
- */
246
- declare class ChatCompletions {
247
- private client;
248
- private apiUrl;
249
- private timeout;
250
- constructor(client: LLMClient, apiUrl: string, timeout: number);
251
- /**
252
- * Create a chat completion (OpenAI-compatible).
253
- */
254
- create(params: OpenAIChatCompletionParams): Promise<OpenAIChatCompletionResponse>;
255
- create(params: OpenAIChatCompletionParams & {
256
- stream: true;
257
- }): Promise<AsyncIterable<OpenAIChatCompletionChunk>>;
258
- private createStream;
259
- private transformResponse;
260
- }
261
- /**
262
- * Chat API namespace
263
- */
264
- declare class Chat {
265
- completions: ChatCompletions;
266
- constructor(client: LLMClient, apiUrl: string, timeout: number);
267
- }
268
- /**
269
- * OpenAI-compatible client for BlockRun.
270
- *
271
- * Drop-in replacement for the OpenAI SDK.
272
- *
273
- * @example
274
- * import { OpenAI } from '@blockrun/llm';
275
- *
276
- * const client = new OpenAI({ walletKey: '0x...' });
277
- *
278
- * const response = await client.chat.completions.create({
279
- * model: 'gpt-4o',
280
- * messages: [{ role: 'user', content: 'Hello!' }]
281
- * });
282
- *
283
- * console.log(response.choices[0].message.content);
284
- */
285
- declare class OpenAI {
286
- chat: Chat;
287
- private client;
288
- constructor(options?: OpenAIClientOptions);
289
- /**
290
- * Get the wallet address being used for payments.
291
- */
292
- getWalletAddress(): string;
293
- }
294
-
295
- export { APIError, BASE_CHAIN_ID, BlockrunError, type ChatChoice, type ChatCompletionOptions, type ChatMessage, type ChatOptions, type ChatResponse, type ChatUsage, LLMClient, type LLMClientOptions, type Model, OpenAI, type OpenAIChatCompletionChoice, type OpenAIChatCompletionChunk, type OpenAIChatCompletionParams, type OpenAIChatCompletionResponse, type OpenAIClientOptions, PaymentError, USDC_BASE, LLMClient as default };