@blockrun/llm 2.1.0 → 2.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/README.md CHANGED
@@ -383,6 +383,35 @@ const r2 = await client.generate('the subject turns and smiles', {
383
383
  });
384
384
  ```
385
385
 
386
+ ### Voice Calls
387
+
388
+ `VoiceClient` wraps `POST /v1/voice/call` (paid, $0.54/call) and
389
+ `GET /v1/voice/call/{callId}` (free polling) — AI-powered outbound phone
390
+ calls powered by Bland.ai. The agent dials the recipient and runs a real-time
391
+ conversation based on your `task` instructions. US + Canada destinations.
392
+
393
+ ```ts
394
+ import { VoiceClient } from '@blockrun/llm';
395
+
396
+ const client = new VoiceClient();
397
+
398
+ // Initiate (paid $0.54)
399
+ const result = await client.call({
400
+ to: '+14155552671',
401
+ task: 'You are a friendly assistant calling to confirm a 3pm dentist appointment.',
402
+ voice: 'maya', // 'nat' | 'josh' | 'maya' | 'june' | 'paige' | 'derek' | 'florian'
403
+ max_duration: 5, // minutes (1–30)
404
+ });
405
+ console.log(result.call_id);
406
+
407
+ // Poll for transcript + recording (free)
408
+ const status = await client.getStatus(result.call_id);
409
+ console.log(status.status, status.recording_url);
410
+ ```
411
+
412
+ Bring your own caller-ID: pass `from: '+14155552671'` (must be a BlockRun
413
+ phone number you own; buy via `/v1/phone/numbers/buy`).
414
+
386
415
  ### Standalone Search
387
416
 
388
417
  `SearchClient` wraps `POST /v1/search` — standalone Grok Live Search.
package/dist/index.cjs CHANGED
@@ -48,6 +48,8 @@ __export(index_exports, {
48
48
  USDC_BASE: () => USDC_BASE,
49
49
  USDC_BASE_CONTRACT: () => USDC_BASE_CONTRACT,
50
50
  USDC_SOLANA: () => USDC_SOLANA,
51
+ VideoClient: () => VideoClient,
52
+ VoiceClient: () => VoiceClient,
51
53
  WALLET_DIR_PATH: () => WALLET_DIR_PATH,
52
54
  WALLET_FILE_PATH: () => WALLET_FILE_PATH,
53
55
  XClient: () => XClient,
@@ -619,9 +621,6 @@ var LLMClient = class _LLMClient {
619
621
  *
620
622
  * @example With routing profile
621
623
  * ```ts
622
- * // Free tier only (zero cost)
623
- * const result = await client.smartChat('Hello!', { routingProfile: 'free' });
624
- *
625
624
  * // Eco mode (budget optimized)
626
625
  * const result = await client.smartChat('Explain quantum computing', { routingProfile: 'eco' });
627
626
  *
@@ -2366,15 +2365,21 @@ var MusicClient = class {
2366
2365
  }
2367
2366
  };
2368
2367
 
2369
- // src/search.ts
2368
+ // src/video.ts
2370
2369
  var import_accounts5 = require("viem/accounts");
2371
2370
  var DEFAULT_API_URL4 = "https://blockrun.ai/api";
2372
- var DEFAULT_TIMEOUT4 = 6e4;
2373
- var SearchClient = class {
2371
+ var DEFAULT_MODEL3 = "xai/grok-imagine-video";
2372
+ var DEFAULT_TIMEOUT4 = 12e4;
2373
+ var POLL_INTERVAL_MS2 = 5e3;
2374
+ var DEFAULT_GENERATE_BUDGET_MS = 3e5;
2375
+ var MAX_TIMEOUT_SECONDS = 600;
2376
+ var VideoClient = class {
2374
2377
  account;
2375
2378
  privateKey;
2376
2379
  apiUrl;
2377
2380
  timeout;
2381
+ sessionTotalUsd = 0;
2382
+ sessionCalls = 0;
2378
2383
  constructor(options = {}) {
2379
2384
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
2380
2385
  const privateKey = options.privateKey || envKey;
@@ -2391,6 +2396,394 @@ var SearchClient = class {
2391
2396
  this.apiUrl = apiUrl.replace(/\/$/, "");
2392
2397
  this.timeout = options.timeout || DEFAULT_TIMEOUT4;
2393
2398
  }
2399
+ /**
2400
+ * Generate a short video clip from a text prompt (or text + image).
2401
+ *
2402
+ * Submits an async job, then polls until the video is ready. Typical total
2403
+ * wall-time is 60-180s. If upstream runs past the budget (default 5min),
2404
+ * throws without charging.
2405
+ *
2406
+ * @param prompt - Text description of the video
2407
+ * @param options - Optional generation parameters
2408
+ */
2409
+ async generate(prompt, options) {
2410
+ const body = {
2411
+ model: options?.model || DEFAULT_MODEL3,
2412
+ prompt
2413
+ };
2414
+ if (options?.imageUrl) body.image_url = options.imageUrl;
2415
+ if (options?.durationSeconds !== void 0) body.duration_seconds = options.durationSeconds;
2416
+ const budgetMs = options?.budgetMs ?? DEFAULT_GENERATE_BUDGET_MS;
2417
+ return this.submitAndPoll(body, budgetMs);
2418
+ }
2419
+ // --------------------------------------------------------------------
2420
+ // Internal: async submit + poll
2421
+ // --------------------------------------------------------------------
2422
+ async submitAndPoll(body, budgetMs) {
2423
+ const submitUrl = `${this.apiUrl}/v1/videos/generations`;
2424
+ const resp402 = await this.fetchWithTimeout(submitUrl, {
2425
+ method: "POST",
2426
+ headers: { "Content-Type": "application/json" },
2427
+ body: JSON.stringify(body)
2428
+ });
2429
+ if (resp402.status !== 402) {
2430
+ await this.throwApiError(resp402, "Expected 402 on first POST");
2431
+ }
2432
+ const paymentRequired = await this.extractPaymentRequired(resp402);
2433
+ const details = extractPaymentDetails(paymentRequired);
2434
+ const paymentPayload = await createPaymentPayload(
2435
+ this.privateKey,
2436
+ this.account.address,
2437
+ details.recipient,
2438
+ details.amount,
2439
+ details.network || "eip155:8453",
2440
+ {
2441
+ resourceUrl: details.resource?.url || submitUrl,
2442
+ resourceDescription: details.resource?.description || "BlockRun Video Generation",
2443
+ // Ensure signed auth covers the entire polling window.
2444
+ maxTimeoutSeconds: Math.max(details.maxTimeoutSeconds || 0, MAX_TIMEOUT_SECONDS),
2445
+ extra: details.extra
2446
+ }
2447
+ );
2448
+ const submitResp = await this.fetchWithTimeout(submitUrl, {
2449
+ method: "POST",
2450
+ headers: {
2451
+ "Content-Type": "application/json",
2452
+ "PAYMENT-SIGNATURE": paymentPayload
2453
+ },
2454
+ body: JSON.stringify(body)
2455
+ });
2456
+ if (submitResp.status === 402) {
2457
+ throw new PaymentError("Payment was rejected. Check your wallet balance.");
2458
+ }
2459
+ if (submitResp.status !== 200 && submitResp.status !== 202) {
2460
+ await this.throwApiError(submitResp, "Submit failed");
2461
+ }
2462
+ const submitData = await submitResp.json();
2463
+ if (!submitData.id || !submitData.poll_url) {
2464
+ throw new APIError(
2465
+ "Submit response missing id/poll_url",
2466
+ submitResp.status,
2467
+ { response: submitData }
2468
+ );
2469
+ }
2470
+ const pollUrl = this.absolute(submitData.poll_url);
2471
+ const deadline = Date.now() + budgetMs;
2472
+ let lastStatus = submitData.status || "queued";
2473
+ while (Date.now() < deadline) {
2474
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS2));
2475
+ const pollResp = await this.fetchWithTimeout(pollUrl, {
2476
+ method: "GET",
2477
+ headers: { "PAYMENT-SIGNATURE": paymentPayload }
2478
+ });
2479
+ let pollData = {};
2480
+ try {
2481
+ pollData = await pollResp.json();
2482
+ } catch {
2483
+ }
2484
+ lastStatus = pollData.status || lastStatus;
2485
+ if (pollResp.status === 202 && (lastStatus === "queued" || lastStatus === "in_progress")) {
2486
+ continue;
2487
+ }
2488
+ if (lastStatus === "failed") {
2489
+ throw new APIError(
2490
+ `Upstream generation failed: ${pollData.error || "unknown"}`,
2491
+ pollResp.status,
2492
+ sanitizeErrorResponse(pollData)
2493
+ );
2494
+ }
2495
+ if (pollResp.status === 200 && lastStatus === "completed") {
2496
+ const data = pollData;
2497
+ const billedSeconds = typeof body.duration_seconds === "number" ? body.duration_seconds : 8;
2498
+ this.sessionCalls++;
2499
+ this.sessionTotalUsd += 0.05 * billedSeconds * 1.05;
2500
+ const txHash = pollResp.headers.get("x-payment-receipt") || pollResp.headers.get("X-Payment-Receipt");
2501
+ if (txHash) data.txHash = txHash;
2502
+ return data;
2503
+ }
2504
+ if (pollResp.status !== 200 && pollResp.status !== 202 && pollResp.status !== 504) {
2505
+ await this.throwApiError(pollResp, "Poll failed");
2506
+ }
2507
+ }
2508
+ throw new APIError(
2509
+ `Video generation did not complete within ${Math.round(budgetMs / 1e3)}s (last status: ${lastStatus}). No payment was taken.`,
2510
+ 504,
2511
+ { id: submitData.id, last_status: lastStatus }
2512
+ );
2513
+ }
2514
+ absolute(url) {
2515
+ if (url.startsWith("http://") || url.startsWith("https://")) return url;
2516
+ const base = this.apiUrl.endsWith("/api") ? this.apiUrl.slice(0, -"/api".length) : this.apiUrl;
2517
+ return `${base}${url}`;
2518
+ }
2519
+ async extractPaymentRequired(resp) {
2520
+ const header = resp.headers.get("payment-required");
2521
+ if (header) return parsePaymentRequired(header);
2522
+ try {
2523
+ const body = await resp.json();
2524
+ if (body && (body.x402Version !== void 0 || body.accepts !== void 0)) {
2525
+ return body;
2526
+ }
2527
+ } catch {
2528
+ }
2529
+ throw new PaymentError("402 response but no payment requirements found");
2530
+ }
2531
+ async throwApiError(resp, prefix) {
2532
+ let errorBody;
2533
+ try {
2534
+ errorBody = await resp.json();
2535
+ } catch {
2536
+ errorBody = { error: "Request failed" };
2537
+ }
2538
+ throw new APIError(
2539
+ `${prefix}: HTTP ${resp.status}`,
2540
+ resp.status,
2541
+ sanitizeErrorResponse(errorBody)
2542
+ );
2543
+ }
2544
+ async fetchWithTimeout(url, options) {
2545
+ const controller = new AbortController();
2546
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
2547
+ try {
2548
+ return await fetch(url, { ...options, signal: controller.signal });
2549
+ } finally {
2550
+ clearTimeout(timeoutId);
2551
+ }
2552
+ }
2553
+ getWalletAddress() {
2554
+ return this.account.address;
2555
+ }
2556
+ getSpending() {
2557
+ return { totalUsd: this.sessionTotalUsd, calls: this.sessionCalls };
2558
+ }
2559
+ };
2560
+
2561
+ // src/voice.ts
2562
+ var import_accounts6 = require("viem/accounts");
2563
+ var DEFAULT_API_URL5 = "https://blockrun.ai/api";
2564
+ var DEFAULT_TIMEOUT5 = 6e4;
2565
+ var CALL_PRICE_USD = 0.54;
2566
+ var VALID_MODELS = /* @__PURE__ */ new Set(["base", "enhanced", "turbo"]);
2567
+ var VoiceClient = class {
2568
+ account;
2569
+ privateKey;
2570
+ apiUrl;
2571
+ timeout;
2572
+ sessionTotalUsd = 0;
2573
+ sessionCalls = 0;
2574
+ constructor(options = {}) {
2575
+ const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
2576
+ const privateKey = options.privateKey || envKey;
2577
+ if (!privateKey) {
2578
+ throw new Error(
2579
+ "Private key required. Pass privateKey in options or set BLOCKRUN_WALLET_KEY environment variable."
2580
+ );
2581
+ }
2582
+ validatePrivateKey(privateKey);
2583
+ this.privateKey = privateKey;
2584
+ this.account = (0, import_accounts6.privateKeyToAccount)(privateKey);
2585
+ const apiUrl = options.apiUrl || DEFAULT_API_URL5;
2586
+ validateApiUrl(apiUrl);
2587
+ this.apiUrl = apiUrl.replace(/\/$/, "");
2588
+ this.timeout = options.timeout || DEFAULT_TIMEOUT5;
2589
+ }
2590
+ /**
2591
+ * Initiate an AI-powered outbound phone call.
2592
+ *
2593
+ * Pricing: $0.54 per call. Returns immediately once the call is queued —
2594
+ * poll getStatus() for transcript and recording.
2595
+ *
2596
+ * @example
2597
+ * const r = await client.call({
2598
+ * to: '+14155552671',
2599
+ * task: 'Confirm the user wants to reschedule to Tuesday 2pm.',
2600
+ * voice: 'maya',
2601
+ * max_duration: 3,
2602
+ * });
2603
+ */
2604
+ async call(options) {
2605
+ if (!options.to || !options.to.trim()) {
2606
+ throw new Error("'to' phone number is required (E.164 format)");
2607
+ }
2608
+ const task = options.task?.trim() ?? "";
2609
+ if (task.length < 10) {
2610
+ throw new Error("'task' must be at least 10 characters");
2611
+ }
2612
+ if (task.length > 4e3) {
2613
+ throw new Error("'task' must be at most 4000 characters");
2614
+ }
2615
+ const maxDuration = options.max_duration ?? 5;
2616
+ if (maxDuration < 1 || maxDuration > 30) {
2617
+ throw new Error("max_duration must be between 1 and 30 minutes");
2618
+ }
2619
+ if (options.model && !VALID_MODELS.has(options.model)) {
2620
+ throw new Error("model must be 'base' | 'enhanced' | 'turbo'");
2621
+ }
2622
+ if (options.interruption_threshold !== void 0 && (options.interruption_threshold < 50 || options.interruption_threshold > 500)) {
2623
+ throw new Error("interruption_threshold must be between 50 and 500");
2624
+ }
2625
+ const body = {
2626
+ to: options.to.trim(),
2627
+ task,
2628
+ max_duration: maxDuration,
2629
+ language: options.language ?? "en-US"
2630
+ };
2631
+ if (options.from) body.from = options.from.trim();
2632
+ if (options.voice) body.voice = options.voice;
2633
+ if (options.first_sentence) body.first_sentence = options.first_sentence.trim();
2634
+ if (options.wait_for_greeting !== void 0) body.wait_for_greeting = options.wait_for_greeting;
2635
+ if (options.interruption_threshold !== void 0) body.interruption_threshold = options.interruption_threshold;
2636
+ if (options.model) body.model = options.model;
2637
+ return this.requestWithPayment("/v1/voice/call", body);
2638
+ }
2639
+ /**
2640
+ * Poll the status of an in-progress or completed call. Free — no payment.
2641
+ *
2642
+ * Returns Bland.ai's full call record: status, transcript, recording URL, etc.
2643
+ * Most fields populate only once the call ends.
2644
+ */
2645
+ async getStatus(callId) {
2646
+ if (!callId || !callId.trim()) {
2647
+ throw new Error("callId is required");
2648
+ }
2649
+ const url = `${this.apiUrl}/v1/voice/call/${encodeURIComponent(callId.trim())}`;
2650
+ const response = await this.fetchWithTimeout(url, {
2651
+ method: "GET",
2652
+ headers: { Accept: "application/json" }
2653
+ });
2654
+ if (response.status === 404) {
2655
+ throw new APIError(`Call not found: ${callId}`, 404, { call_id: callId });
2656
+ }
2657
+ if (!response.ok) {
2658
+ let errorBody;
2659
+ try {
2660
+ errorBody = await response.json();
2661
+ } catch {
2662
+ errorBody = { error: "Request failed" };
2663
+ }
2664
+ throw new APIError(`API error: ${response.status}`, response.status, sanitizeErrorResponse(errorBody));
2665
+ }
2666
+ return response.json();
2667
+ }
2668
+ async requestWithPayment(endpoint, body) {
2669
+ const url = `${this.apiUrl}${endpoint}`;
2670
+ const response = await this.fetchWithTimeout(url, {
2671
+ method: "POST",
2672
+ headers: { "Content-Type": "application/json" },
2673
+ body: JSON.stringify(body)
2674
+ });
2675
+ if (response.status === 402) {
2676
+ return this.handlePaymentAndRetry(url, body, response);
2677
+ }
2678
+ if (!response.ok) {
2679
+ let errorBody;
2680
+ try {
2681
+ errorBody = await response.json();
2682
+ } catch {
2683
+ errorBody = { error: "Request failed" };
2684
+ }
2685
+ throw new APIError(`API error: ${response.status}`, response.status, sanitizeErrorResponse(errorBody));
2686
+ }
2687
+ return response.json();
2688
+ }
2689
+ async handlePaymentAndRetry(url, body, response) {
2690
+ let paymentHeader = response.headers.get("payment-required");
2691
+ if (!paymentHeader) {
2692
+ try {
2693
+ const respBody = await response.json();
2694
+ if (respBody.x402 || respBody.accepts) {
2695
+ paymentHeader = btoa(JSON.stringify(respBody));
2696
+ }
2697
+ } catch {
2698
+ }
2699
+ }
2700
+ if (!paymentHeader) {
2701
+ throw new PaymentError("402 response but no payment requirements found");
2702
+ }
2703
+ const paymentRequired = parsePaymentRequired(paymentHeader);
2704
+ const details = extractPaymentDetails(paymentRequired);
2705
+ const paymentPayload = await createPaymentPayload(
2706
+ this.privateKey,
2707
+ this.account.address,
2708
+ details.recipient,
2709
+ details.amount,
2710
+ details.network || "eip155:8453",
2711
+ {
2712
+ resourceUrl: details.resource?.url || `${this.apiUrl}/v1/voice/call`,
2713
+ resourceDescription: details.resource?.description || "BlockRun Voice Call",
2714
+ maxTimeoutSeconds: details.maxTimeoutSeconds || 300,
2715
+ extra: details.extra
2716
+ }
2717
+ );
2718
+ const retryResponse = await this.fetchWithTimeout(url, {
2719
+ method: "POST",
2720
+ headers: {
2721
+ "Content-Type": "application/json",
2722
+ "PAYMENT-SIGNATURE": paymentPayload
2723
+ },
2724
+ body: JSON.stringify(body)
2725
+ });
2726
+ if (retryResponse.status === 402) {
2727
+ throw new PaymentError("Payment was rejected. Check your wallet balance.");
2728
+ }
2729
+ if (!retryResponse.ok) {
2730
+ let errorBody;
2731
+ try {
2732
+ errorBody = await retryResponse.json();
2733
+ } catch {
2734
+ errorBody = { error: "Request failed" };
2735
+ }
2736
+ throw new APIError(`API error after payment: ${retryResponse.status}`, retryResponse.status, sanitizeErrorResponse(errorBody));
2737
+ }
2738
+ const data = await retryResponse.json();
2739
+ this.sessionCalls++;
2740
+ this.sessionTotalUsd += CALL_PRICE_USD;
2741
+ const txHash = retryResponse.headers.get("x-payment-receipt") || retryResponse.headers.get("X-Payment-Receipt");
2742
+ if (txHash) data.txHash = txHash;
2743
+ return data;
2744
+ }
2745
+ async fetchWithTimeout(url, options) {
2746
+ const controller = new AbortController();
2747
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
2748
+ try {
2749
+ return await fetch(url, { ...options, signal: controller.signal });
2750
+ } finally {
2751
+ clearTimeout(timeoutId);
2752
+ }
2753
+ }
2754
+ getWalletAddress() {
2755
+ return this.account.address;
2756
+ }
2757
+ getSpending() {
2758
+ return { totalUsd: this.sessionTotalUsd, calls: this.sessionCalls };
2759
+ }
2760
+ };
2761
+
2762
+ // src/search.ts
2763
+ var import_accounts7 = require("viem/accounts");
2764
+ var DEFAULT_API_URL6 = "https://blockrun.ai/api";
2765
+ var DEFAULT_TIMEOUT6 = 6e4;
2766
+ var SearchClient = class {
2767
+ account;
2768
+ privateKey;
2769
+ apiUrl;
2770
+ timeout;
2771
+ constructor(options = {}) {
2772
+ const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
2773
+ const privateKey = options.privateKey || envKey;
2774
+ if (!privateKey) {
2775
+ throw new Error(
2776
+ "Private key required. Pass privateKey in options or set BLOCKRUN_WALLET_KEY environment variable."
2777
+ );
2778
+ }
2779
+ validatePrivateKey(privateKey);
2780
+ this.privateKey = privateKey;
2781
+ this.account = (0, import_accounts7.privateKeyToAccount)(privateKey);
2782
+ const apiUrl = options.apiUrl || DEFAULT_API_URL6;
2783
+ validateApiUrl(apiUrl);
2784
+ this.apiUrl = apiUrl.replace(/\/$/, "");
2785
+ this.timeout = options.timeout || DEFAULT_TIMEOUT6;
2786
+ }
2394
2787
  async search(query, options) {
2395
2788
  if (!query || query.length > 1e3) {
2396
2789
  throw new Error("query must be 1-1000 characters");
@@ -2503,9 +2896,9 @@ var SearchClient = class {
2503
2896
  };
2504
2897
 
2505
2898
  // src/x-client.ts
2506
- var import_accounts6 = require("viem/accounts");
2507
- var DEFAULT_API_URL5 = "https://blockrun.ai/api";
2508
- var DEFAULT_TIMEOUT5 = 6e4;
2899
+ var import_accounts8 = require("viem/accounts");
2900
+ var DEFAULT_API_URL7 = "https://blockrun.ai/api";
2901
+ var DEFAULT_TIMEOUT7 = 6e4;
2509
2902
  var XClient = class _XClient {
2510
2903
  account;
2511
2904
  privateKey;
@@ -2528,11 +2921,11 @@ var XClient = class _XClient {
2528
2921
  }
2529
2922
  validatePrivateKey(privateKey);
2530
2923
  this.privateKey = privateKey;
2531
- this.account = (0, import_accounts6.privateKeyToAccount)(privateKey);
2532
- const apiUrl = options.apiUrl || DEFAULT_API_URL5;
2924
+ this.account = (0, import_accounts8.privateKeyToAccount)(privateKey);
2925
+ const apiUrl = options.apiUrl || DEFAULT_API_URL7;
2533
2926
  validateApiUrl(apiUrl);
2534
2927
  this.apiUrl = apiUrl.replace(/\/$/, "");
2535
- this.timeout = options.timeout || DEFAULT_TIMEOUT5;
2928
+ this.timeout = options.timeout || DEFAULT_TIMEOUT7;
2536
2929
  }
2537
2930
  // ───────── User endpoints ─────────
2538
2931
  userLookup(usernames) {
@@ -2710,9 +3103,9 @@ var XClient = class _XClient {
2710
3103
  };
2711
3104
 
2712
3105
  // src/price.ts
2713
- var import_accounts7 = require("viem/accounts");
2714
- var DEFAULT_API_URL6 = "https://blockrun.ai/api";
2715
- var DEFAULT_TIMEOUT6 = 3e4;
3106
+ var import_accounts9 = require("viem/accounts");
3107
+ var DEFAULT_API_URL8 = "https://blockrun.ai/api";
3108
+ var DEFAULT_TIMEOUT8 = 3e4;
2716
3109
  var PriceClient = class {
2717
3110
  account = null;
2718
3111
  privateKey = null;
@@ -2730,12 +3123,12 @@ var PriceClient = class {
2730
3123
  if (privateKey) {
2731
3124
  validatePrivateKey(privateKey);
2732
3125
  this.privateKey = privateKey;
2733
- this.account = (0, import_accounts7.privateKeyToAccount)(privateKey);
3126
+ this.account = (0, import_accounts9.privateKeyToAccount)(privateKey);
2734
3127
  }
2735
- const apiUrl = options.apiUrl || DEFAULT_API_URL6;
3128
+ const apiUrl = options.apiUrl || DEFAULT_API_URL8;
2736
3129
  validateApiUrl(apiUrl);
2737
3130
  this.apiUrl = apiUrl.replace(/\/$/, "");
2738
- this.timeout = options.timeout || DEFAULT_TIMEOUT6;
3131
+ this.timeout = options.timeout || DEFAULT_TIMEOUT8;
2739
3132
  }
2740
3133
  async price(category, symbol, options) {
2741
3134
  if (!symbol) throw new Error("symbol is required");
@@ -2914,7 +3307,7 @@ function buildUrl(base, query) {
2914
3307
  }
2915
3308
 
2916
3309
  // src/wallet.ts
2917
- var import_accounts8 = require("viem/accounts");
3310
+ var import_accounts10 = require("viem/accounts");
2918
3311
  var fs2 = __toESM(require("fs"), 1);
2919
3312
  var path2 = __toESM(require("path"), 1);
2920
3313
  var os2 = __toESM(require("os"), 1);
@@ -2923,8 +3316,8 @@ var BASE_CHAIN_ID2 = "8453";
2923
3316
  var WALLET_DIR = path2.join(os2.homedir(), ".blockrun");
2924
3317
  var WALLET_FILE = path2.join(WALLET_DIR, ".session");
2925
3318
  function createWallet() {
2926
- const privateKey = (0, import_accounts8.generatePrivateKey)();
2927
- const account = (0, import_accounts8.privateKeyToAccount)(privateKey);
3319
+ const privateKey = (0, import_accounts10.generatePrivateKey)();
3320
+ const account = (0, import_accounts10.privateKeyToAccount)(privateKey);
2928
3321
  return {
2929
3322
  address: account.address,
2930
3323
  privateKey
@@ -2980,12 +3373,12 @@ function loadWallet() {
2980
3373
  function getOrCreateWallet() {
2981
3374
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
2982
3375
  if (envKey) {
2983
- const account = (0, import_accounts8.privateKeyToAccount)(envKey);
3376
+ const account = (0, import_accounts10.privateKeyToAccount)(envKey);
2984
3377
  return { address: account.address, privateKey: envKey, isNew: false };
2985
3378
  }
2986
3379
  const fileKey = loadWallet();
2987
3380
  if (fileKey) {
2988
- const account = (0, import_accounts8.privateKeyToAccount)(fileKey);
3381
+ const account = (0, import_accounts10.privateKeyToAccount)(fileKey);
2989
3382
  return { address: account.address, privateKey: fileKey, isNew: false };
2990
3383
  }
2991
3384
  const { address, privateKey } = createWallet();
@@ -2995,11 +3388,11 @@ function getOrCreateWallet() {
2995
3388
  function getWalletAddress() {
2996
3389
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
2997
3390
  if (envKey) {
2998
- return (0, import_accounts8.privateKeyToAccount)(envKey).address;
3391
+ return (0, import_accounts10.privateKeyToAccount)(envKey).address;
2999
3392
  }
3000
3393
  const fileKey = loadWallet();
3001
3394
  if (fileKey) {
3002
- return (0, import_accounts8.privateKeyToAccount)(fileKey).address;
3395
+ return (0, import_accounts10.privateKeyToAccount)(fileKey).address;
3003
3396
  }
3004
3397
  return null;
3005
3398
  }
@@ -3179,7 +3572,7 @@ async function getOrCreateSolanaWallet() {
3179
3572
  // src/solana-client.ts
3180
3573
  var SOLANA_API_URL = "https://sol.blockrun.ai/api";
3181
3574
  var DEFAULT_MAX_TOKENS2 = 1024;
3182
- var DEFAULT_TIMEOUT7 = 6e4;
3575
+ var DEFAULT_TIMEOUT9 = 6e4;
3183
3576
  var SDK_VERSION2 = "0.3.0";
3184
3577
  var USER_AGENT2 = `blockrun-ts/${SDK_VERSION2}`;
3185
3578
  var SolanaLLMClient = class {
@@ -3204,7 +3597,7 @@ var SolanaLLMClient = class {
3204
3597
  validateApiUrl(apiUrl);
3205
3598
  this.apiUrl = apiUrl.replace(/\/$/, "");
3206
3599
  this.rpcUrl = options.rpcUrl || "https://api.mainnet-beta.solana.com";
3207
- this.timeout = options.timeout || DEFAULT_TIMEOUT7;
3600
+ this.timeout = options.timeout || DEFAULT_TIMEOUT9;
3208
3601
  }
3209
3602
  /** Get Solana wallet address (public key in base58). */
3210
3603
  async getWalletAddress() {
@@ -4029,6 +4422,9 @@ var ChatCompletions = class {
4029
4422
  this.apiUrl = apiUrl;
4030
4423
  this.timeout = timeout;
4031
4424
  }
4425
+ client;
4426
+ apiUrl;
4427
+ timeout;
4032
4428
  async create(params) {
4033
4429
  if (params.stream) {
4034
4430
  return this.createStream(params);
@@ -4108,7 +4504,7 @@ var OpenAI = class {
4108
4504
  };
4109
4505
 
4110
4506
  // src/anthropic-compat.ts
4111
- var import_accounts9 = require("viem/accounts");
4507
+ var import_accounts11 = require("viem/accounts");
4112
4508
  var AnthropicClient = class {
4113
4509
  _client = null;
4114
4510
  _clientPromise = null;
@@ -4121,7 +4517,7 @@ var AnthropicClient = class {
4121
4517
  const key = options.privateKey ?? wallet.privateKey;
4122
4518
  validatePrivateKey(key);
4123
4519
  this._privateKey = key;
4124
- this._account = (0, import_accounts9.privateKeyToAccount)(this._privateKey);
4520
+ this._account = (0, import_accounts11.privateKeyToAccount)(this._privateKey);
4125
4521
  const apiUrl = options.apiUrl ?? "https://blockrun.ai/api";
4126
4522
  validateApiUrl(apiUrl);
4127
4523
  this._apiUrl = apiUrl.replace(/\/$/, "");
@@ -4235,6 +4631,8 @@ var AnthropicClient = class {
4235
4631
  USDC_BASE,
4236
4632
  USDC_BASE_CONTRACT,
4237
4633
  USDC_SOLANA,
4634
+ VideoClient,
4635
+ VoiceClient,
4238
4636
  WALLET_DIR_PATH,
4239
4637
  WALLET_FILE_PATH,
4240
4638
  XClient,