@blockrun/llm 1.15.0 → 2.1.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/dist/index.d.cts CHANGED
@@ -58,6 +58,25 @@ interface ChatResponse {
58
58
  choices: ChatChoice[];
59
59
  usage?: ChatUsage;
60
60
  citations?: string[];
61
+ /**
62
+ * Populated when the gateway transparently substituted a different
63
+ * model for the one the caller asked for — typically because the
64
+ * requested model errored and the gateway routed to a free fallback
65
+ * to fulfil the request. When `used` is true:
66
+ * - `model` is the model that actually answered (vs `ChatResponse.model`
67
+ * which historically reflected the requested model id).
68
+ * - `settlementSkipped` is `true` when the gateway also skipped the
69
+ * on-chain settle — i.e. the user was not charged for this call
70
+ * because a free fallback served it.
71
+ * Surfaced from the gateway's `X-Fallback-Used / X-Fallback-Model /
72
+ * X-Settlement-Skipped` response headers. Absent when the headers
73
+ * aren't present (most calls).
74
+ */
75
+ fallback?: {
76
+ used: true;
77
+ model?: string;
78
+ settlementSkipped?: boolean;
79
+ };
61
80
  }
62
81
  interface Model {
63
82
  id: string;
@@ -242,6 +261,13 @@ interface ChatOptions {
242
261
  search?: boolean;
243
262
  /** Full Live Search configuration (for search-enabled models) */
244
263
  searchParameters?: SearchParameters;
264
+ /**
265
+ * Models to try in order if the primary returns a transient error
266
+ * (timeout, network, 5xx). 4xx and PaymentError still propagate
267
+ * immediately. `smartChat` populates this from the routing tier's
268
+ * fallback chain automatically.
269
+ */
270
+ fallbackModels?: string[];
245
271
  }
246
272
  interface ChatCompletionOptions {
247
273
  /** Max tokens to generate */
@@ -258,6 +284,12 @@ interface ChatCompletionOptions {
258
284
  tools?: Tool[];
259
285
  /** Tool selection strategy */
260
286
  toolChoice?: ToolChoice;
287
+ /**
288
+ * Models to try in order if the primary returns a transient error
289
+ * (timeout, network, 5xx). 4xx and PaymentError still propagate
290
+ * immediately.
291
+ */
292
+ fallbackModels?: string[];
261
293
  }
262
294
  type RoutingProfile = "free" | "eco" | "auto" | "premium";
263
295
  type RoutingTier = "SIMPLE" | "MEDIUM" | "COMPLEX" | "REASONING";
@@ -270,6 +302,12 @@ interface RoutingDecision {
270
302
  costEstimate: number;
271
303
  baselineCost: number;
272
304
  savings: number;
305
+ /**
306
+ * Remaining tier models with known pricing, in fallback order. `chat()`
307
+ * walks this list when the primary model hits a transient error
308
+ * (timeout, network, 5xx). Excludes the primary itself.
309
+ */
310
+ fallbacks?: string[];
273
311
  }
274
312
  interface SmartChatOptions extends ChatOptions {
275
313
  /** Routing profile: free (zero cost), eco (budget), auto (balanced), premium (best quality) */
@@ -689,29 +727,11 @@ declare class APIError extends BlockrunError {
689
727
  * BlockRun LLM Gateway Client.
690
728
  *
691
729
  * Provides access to multiple LLM providers (OpenAI, Anthropic, Google, etc.)
692
- * with automatic x402 micropayments on Base chain.
693
- *
694
- * Networks:
695
- * - Mainnet: https://blockrun.ai/api (Base, Chain ID 8453)
696
- * - Testnet: https://testnet.blockrun.ai/api (Base Sepolia, Chain ID 84532)
697
- *
698
- * @example Testnet usage
699
- * ```ts
700
- * // Use testnet convenience function
701
- * import { testnetClient } from '@blockrun/llm';
702
- * const client = testnetClient({ privateKey: '0x...' });
703
- * const response = await client.chat('openai/gpt-oss-20b', 'Hello!');
704
- *
705
- * // Or configure manually
706
- * const client = new LLMClient({
707
- * privateKey: '0x...',
708
- * apiUrl: 'https://testnet.blockrun.ai/api'
709
- * });
710
- * ```
730
+ * with automatic x402 micropayments on Base chain (Mainnet, Chain ID 8453).
731
+ * API base: https://blockrun.ai/api
711
732
  */
712
733
  declare class LLMClient {
713
734
  static readonly DEFAULT_API_URL = "https://blockrun.ai/api";
714
- static readonly TESTNET_API_URL = "https://testnet.blockrun.ai/api";
715
735
  private account;
716
736
  private privateKey;
717
737
  private apiUrl;
@@ -779,17 +799,48 @@ declare class LLMClient {
779
799
  private getModelPricing;
780
800
  /**
781
801
  * Fetch model pricing from API.
802
+ *
803
+ * For flat-billed models (e.g. ZAI GLM-5 family at $0.001/call) the
804
+ * router still expects per-token rates, so we synthesise an equivalent
805
+ * per-token price assuming ~1500 total tokens per call. Without this,
806
+ * flat models would resolve to inputPrice=outputPrice=0 and the router
807
+ * would treat them as free, biasing routing decisions and reporting
808
+ * inflated savings %.
782
809
  */
783
810
  private fetchModelPricing;
784
811
  /**
785
812
  * Full chat completion interface (OpenAI-compatible).
786
813
  *
787
- * @param model - Model ID
814
+ * When `fallbackModels` is set, transient failures (timeouts, network
815
+ * errors, 5xx) on the primary model trigger a retry against the next
816
+ * model in the list before raising. 4xx errors and PaymentError
817
+ * propagate immediately — those aren't "swap upstream and retry"
818
+ * situations. Each fallback hop logs one stderr line.
819
+ *
820
+ * @param model - Primary model ID
788
821
  * @param messages - Array of messages with role and content
789
822
  * @param options - Optional completion parameters
790
823
  * @returns ChatResponse object with choices and usage
791
824
  */
792
825
  chatCompletion(model: string, messages: ChatMessage[], options?: ChatCompletionOptions): Promise<ChatResponse>;
826
+ /**
827
+ * Write a canonical cost_log entry after a settled x402 payment.
828
+ * Best-effort: failures here must never break a successful API call.
829
+ * Mirrors what Franklin's AgentClient writes via src/agent/llm.ts so
830
+ * cost_log.jsonl is a single source of truth regardless of caller.
831
+ */
832
+ private recordCost;
833
+ /**
834
+ * Parse the chat response JSON and attach `fallback` metadata when the
835
+ * gateway signalled a transparent free-fallback substitution. The
836
+ * gateway sets X-Fallback-Used / X-Fallback-Model / X-Settlement-Skipped
837
+ * on the response when it served a paid request from a free model
838
+ * (route.ts createPaymentResponseHeader path). Without surfacing these
839
+ * to the caller, the user gets a different model than requested with
840
+ * no visibility — silent quality drop and no clue why the on-chain
841
+ * balance didn't change.
842
+ */
843
+ private parseChatResponse;
793
844
  /**
794
845
  * Make a request with automatic x402 payment handling.
795
846
  */
@@ -836,29 +887,37 @@ declare class LLMClient {
836
887
  */
837
888
  private fetchWithTimeout;
838
889
  /**
839
- * List available LLM models with pricing.
890
+ * List available models with pricing.
891
+ *
892
+ * Returns the full `/v1/models` unified catalog (chat + image + music).
893
+ * The shape preserves backwards compatibility — image/music rows have
894
+ * `inputPrice = outputPrice = 0` since those fields don't apply, and
895
+ * their per-call price surfaces via `flatPrice`.
840
896
  */
841
897
  listModels(): Promise<Model[]>;
842
898
  /**
843
899
  * List available image generation models with pricing.
900
+ *
901
+ * The dedicated `/v1/images/models` endpoint was deprecated server-side;
902
+ * image models live in the unified `/v1/models` catalog under
903
+ * `categories: ["image", ...]`. This method filters that catalog so
904
+ * existing callers keep working.
844
905
  */
845
906
  listImageModels(): Promise<ImageModel[]>;
846
907
  /**
847
- * List all available models (both LLM and image) with pricing.
908
+ * List all available models (chat, image, music, etc.) with pricing.
848
909
  *
849
- * @returns Array of all models with 'type' field ('llm' or 'image')
850
- *
851
- * @example
852
- * const models = await client.listAllModels();
853
- * for (const model of models) {
854
- * if (model.type === 'llm') {
855
- * console.log(`LLM: ${model.id} - $${model.inputPrice}/M input`);
856
- * } else {
857
- * console.log(`Image: ${model.id} - $${model.pricePerImage}/image`);
858
- * }
859
- * }
910
+ * @returns Array of all models with `type` field set from category
911
+ * (`llm` for chat, `image` / `music` for media). Backwards-compat:
912
+ * chat models always report `type: "llm"`.
860
913
  */
861
914
  listAllModels(): Promise<(Model | ImageModel)[]>;
915
+ /**
916
+ * Internal: fetch the raw `/v1/models` catalog without normalising shape.
917
+ * Used by listImageModels / listAllModels so each can pick category-
918
+ * specific fields.
919
+ */
920
+ private fetchRawModels;
862
921
  /**
863
922
  * Edit an image using img2img.
864
923
  *
@@ -876,6 +935,17 @@ declare class LLMClient {
876
935
  * @returns SearchResult with summary and citations
877
936
  */
878
937
  search(query: string, options?: SearchOptions): Promise<SearchResult>;
938
+ /**
939
+ * Generic Exa endpoint proxy (POST). Useful when you need an Exa API
940
+ * surface that the typed wrappers below don't expose.
941
+ *
942
+ * @param path - Exa endpoint segment: "search" | "find-similar" | "contents" | "answer"
943
+ * @param body - Request body (see Exa API docs)
944
+ *
945
+ * @example
946
+ * const results = await client.exa("search", { query: "latest AI research", numResults: 5 });
947
+ */
948
+ exa(path: string, body: Record<string, unknown>): Promise<Record<string, unknown>>;
879
949
  /**
880
950
  * Neural web search via Exa. Returns semantically relevant URLs and metadata.
881
951
  * Understands meaning, not just keywords. $0.01/call.
@@ -907,9 +977,7 @@ declare class LLMClient {
907
977
  */
908
978
  exaFindSimilar(url: string, options?: ExaFindSimilarOptions): Promise<ExaSearchResponse>;
909
979
  /**
910
- * Get USDC balance on Base network.
911
- *
912
- * Automatically detects mainnet vs testnet based on API URL.
980
+ * Get USDC balance on Base mainnet.
913
981
  *
914
982
  * @returns USDC balance as a float (6 decimal places normalized)
915
983
  *
@@ -1120,38 +1188,7 @@ declare class LLMClient {
1120
1188
  * Get the wallet address being used for payments.
1121
1189
  */
1122
1190
  getWalletAddress(): string;
1123
- /**
1124
- * Check if client is configured for testnet.
1125
- */
1126
- isTestnet(): boolean;
1127
1191
  }
1128
- /**
1129
- * Create a testnet LLM client for development and testing.
1130
- *
1131
- * This is a convenience function that creates an LLMClient configured
1132
- * for the BlockRun testnet (Base Sepolia).
1133
- *
1134
- * @param options - Client options (privateKey required unless BASE_CHAIN_WALLET_KEY env var is set)
1135
- * @returns LLMClient configured for testnet
1136
- *
1137
- * @example
1138
- * ```ts
1139
- * import { testnetClient } from '@blockrun/llm';
1140
- *
1141
- * const client = testnetClient({ privateKey: '0x...' });
1142
- * const response = await client.chat('openai/gpt-oss-20b', 'Hello!');
1143
- * ```
1144
- *
1145
- * Testnet Setup:
1146
- * 1. Get testnet ETH from https://www.alchemy.com/faucets/base-sepolia
1147
- * 2. Get testnet USDC from https://faucet.circle.com/
1148
- * 3. Use your wallet with testnet funds
1149
- *
1150
- * Available Testnet Models:
1151
- * - openai/gpt-oss-20b
1152
- * - openai/gpt-oss-120b
1153
- */
1154
- declare function testnetClient(options?: Omit<LLMClientOptions, 'apiUrl'>): LLMClient;
1155
1192
 
1156
1193
  /**
1157
1194
  * BlockRun Image Client - Generate images via x402 micropayments.
@@ -1216,6 +1253,11 @@ declare class ImageClient {
1216
1253
  edit(prompt: string, image: string, options?: ImageEditOptions): Promise<ImageResponse>;
1217
1254
  /**
1218
1255
  * List available image generation models with pricing.
1256
+ *
1257
+ * The dedicated `/v1/images/models` endpoint was deprecated server-side;
1258
+ * image models live in the unified `/v1/models` catalog under
1259
+ * `categories: ["image", ...]`. This method filters that catalog so
1260
+ * existing callers keep working.
1219
1261
  */
1220
1262
  listImageModels(): Promise<ImageModel[]>;
1221
1263
  /**
@@ -1850,18 +1892,29 @@ declare function status(): Promise<{
1850
1892
  balance: number;
1851
1893
  }>;
1852
1894
 
1895
+ /** Canonical on-wire schema for cost_log.jsonl entries. */
1853
1896
  interface CostEntry {
1854
- timestamp: string;
1855
- model: string;
1856
- inputTokens: number;
1857
- outputTokens: number;
1858
- costUsd: number;
1897
+ /** Unix epoch seconds (float, millisecond precision). */
1898
+ ts: number;
1899
+ /** API endpoint path, e.g. "/v1/chat/completions". */
1900
+ endpoint: string;
1901
+ /** Settled USDC amount (USD, 6-decimal precision). */
1902
+ cost_usd: number;
1903
+ /** Model id when known, e.g. "zai/glm-5-turbo". Optional for non-LLM endpoints. */
1904
+ model?: string;
1905
+ /** Payer wallet address (EVM 0x... or Solana base58). */
1906
+ wallet?: string;
1907
+ /** Network identifier — "eip155:8453" for Base mainnet, "solana-mainnet", etc. */
1908
+ network?: string;
1909
+ /** Caller kind for analytics — "LLMClient", "ImageClient", "AgentClient", ... */
1910
+ client_kind?: string;
1859
1911
  }
1860
1912
  declare function logCost(entry: CostEntry): void;
1861
1913
  declare function getCostSummary(): {
1862
1914
  totalUsd: number;
1863
1915
  calls: number;
1864
1916
  byModel: Record<string, number>;
1917
+ byEndpoint: Record<string, number>;
1865
1918
  };
1866
1919
 
1867
1920
  /**
@@ -2077,4 +2130,4 @@ declare function validateTemperature(temperature?: number): void;
2077
2130
  */
2078
2131
  declare function validateTopP(topP?: number): void;
2079
2132
 
2080
- export { APIError, AnthropicClient, type AudioModel, type AudioTrack, BASE_CHAIN_ID, type BarResolution, type BlockRunAnthropicOptions, BlockrunError, type ChatChoice, type ChatCompletionOptions, type ChatMessage, type ChatOptions, type ChatResponse, type ChatResponseWithCost, type ChatUsage, type CostEntry, type CostEstimate, type CreatePaymentOptions, type FunctionCall, type FunctionDefinition, type HistoryOptions, ImageClient, type ImageClientOptions, type ImageData, type ImageEditOptions, type ImageGenerateOptions, type ImageModel, type ImageResponse, KNOWN_PROVIDERS, LLMClient, type LLMClientOptions, type ListOptions, type MarketSession, type Model, MusicClient, type MusicClientOptions, type MusicGenerateOptions, type MusicResponse, type NewsSearchSource, OpenAI, type OpenAIChatCompletionChoice, type OpenAIChatCompletionChunk, type OpenAIChatCompletionParams, type OpenAIChatCompletionResponse, type OpenAIClientOptions, PaymentError, type PaymentLinks, type PriceBar, type PriceCategory, PriceClient, type PriceClientOptions, type PriceHistoryResponse, type PriceOptions, type PricePoint, type RoutingDecision, type RoutingProfile, type RoutingTier, type RssSearchSource, SOLANA_NETWORK, SOLANA_WALLET_FILE as SOLANA_WALLET_FILE_PATH, SearchClient, type SearchClientOptions, type SearchOptions, type SearchParameters, type SearchResult, type SearchSource, type SearchUsage, type SmartChatOptions, type SmartChatResponse, SolanaLLMClient, type SolanaLLMClientOptions, type SolanaWalletInfo, type Spending, type SpendingReport, type StockMarket, type SymbolListResponse, type Tool, type ToolCall, type ToolChoice, USDC_BASE, USDC_BASE_CONTRACT, USDC_SOLANA, type VideoClientOptions, type VideoClip, type VideoGenerateOptions, type VideoModel, type VideoResponse, WALLET_DIR_PATH, WALLET_FILE_PATH, type WalletInfo, type WebSearchSource, type XArticlesRisingResponse, type XAuthorAnalyticsResponse, XClient, type XClientOptions, type XCompareAuthorsResponse, type XFollower, type XFollowersResponse, type XFollowingsResponse, type XMentionsOptions, type XMentionsResponse, type XSearchOptions, type XSearchResponse, type XSearchSource, type XTrendingResponse, type XTweet, type XTweetLookupResponse, type XTweetRepliesOptions, type XTweetRepliesResponse, type XTweetThreadResponse, type XTweetsResponse, type XUser, type XUserInfoResponse, type XUserLookupResponse, type XUserTweetsOptions, type XVerifiedFollowersResponse, clearCache, createPaymentPayload, createSolanaPaymentPayload, createSolanaWallet, createWallet, LLMClient as default, extractPaymentDetails, formatFundingMessageCompact, formatNeedsFundingMessage, formatWalletCreatedMessage, getCached, getCachedByRequest, getCostLogSummary, getCostSummary, getEip681Uri, getOrCreateSolanaWallet, getOrCreateWallet, getPaymentLinks, getWalletAddress, loadSolanaWallet, loadWallet, logCost, parsePaymentRequired, saveSolanaWallet, saveToCache, saveWallet, scanSolanaWallets, scanWallets, setCache, setupAgentSolanaWallet, setupAgentWallet, solanaClient, solanaKeyToBytes, solanaPublicKey, status, testnetClient, validateMaxTokens, validateModel, validateTemperature, validateTopP };
2133
+ export { APIError, AnthropicClient, type AudioModel, type AudioTrack, BASE_CHAIN_ID, type BarResolution, type BlockRunAnthropicOptions, BlockrunError, type ChatChoice, type ChatCompletionOptions, type ChatMessage, type ChatOptions, type ChatResponse, type ChatResponseWithCost, type ChatUsage, type CostEntry, type CostEstimate, type CreatePaymentOptions, type FunctionCall, type FunctionDefinition, type HistoryOptions, ImageClient, type ImageClientOptions, type ImageData, type ImageEditOptions, type ImageGenerateOptions, type ImageModel, type ImageResponse, KNOWN_PROVIDERS, LLMClient, type LLMClientOptions, type ListOptions, type MarketSession, type Model, MusicClient, type MusicClientOptions, type MusicGenerateOptions, type MusicResponse, type NewsSearchSource, OpenAI, type OpenAIChatCompletionChoice, type OpenAIChatCompletionChunk, type OpenAIChatCompletionParams, type OpenAIChatCompletionResponse, type OpenAIClientOptions, PaymentError, type PaymentLinks, type PriceBar, type PriceCategory, PriceClient, type PriceClientOptions, type PriceHistoryResponse, type PriceOptions, type PricePoint, type RoutingDecision, type RoutingProfile, type RoutingTier, type RssSearchSource, SOLANA_NETWORK, SOLANA_WALLET_FILE as SOLANA_WALLET_FILE_PATH, SearchClient, type SearchClientOptions, type SearchOptions, type SearchParameters, type SearchResult, type SearchSource, type SearchUsage, type SmartChatOptions, type SmartChatResponse, SolanaLLMClient, type SolanaLLMClientOptions, type SolanaWalletInfo, type Spending, type SpendingReport, type StockMarket, type SymbolListResponse, type Tool, type ToolCall, type ToolChoice, USDC_BASE, USDC_BASE_CONTRACT, USDC_SOLANA, type VideoClientOptions, type VideoClip, type VideoGenerateOptions, type VideoModel, type VideoResponse, WALLET_DIR_PATH, WALLET_FILE_PATH, type WalletInfo, type WebSearchSource, type XArticlesRisingResponse, type XAuthorAnalyticsResponse, XClient, type XClientOptions, type XCompareAuthorsResponse, type XFollower, type XFollowersResponse, type XFollowingsResponse, type XMentionsOptions, type XMentionsResponse, type XSearchOptions, type XSearchResponse, type XSearchSource, type XTrendingResponse, type XTweet, type XTweetLookupResponse, type XTweetRepliesOptions, type XTweetRepliesResponse, type XTweetThreadResponse, type XTweetsResponse, type XUser, type XUserInfoResponse, type XUserLookupResponse, type XUserTweetsOptions, type XVerifiedFollowersResponse, clearCache, createPaymentPayload, createSolanaPaymentPayload, createSolanaWallet, createWallet, LLMClient as default, extractPaymentDetails, formatFundingMessageCompact, formatNeedsFundingMessage, formatWalletCreatedMessage, getCached, getCachedByRequest, getCostLogSummary, getCostSummary, getEip681Uri, getOrCreateSolanaWallet, getOrCreateWallet, getPaymentLinks, getWalletAddress, loadSolanaWallet, loadWallet, logCost, parsePaymentRequired, saveSolanaWallet, saveToCache, saveWallet, scanSolanaWallets, scanWallets, setCache, setupAgentSolanaWallet, setupAgentWallet, solanaClient, solanaKeyToBytes, solanaPublicKey, status, validateMaxTokens, validateModel, validateTemperature, validateTopP };
package/dist/index.d.ts CHANGED
@@ -58,6 +58,25 @@ interface ChatResponse {
58
58
  choices: ChatChoice[];
59
59
  usage?: ChatUsage;
60
60
  citations?: string[];
61
+ /**
62
+ * Populated when the gateway transparently substituted a different
63
+ * model for the one the caller asked for — typically because the
64
+ * requested model errored and the gateway routed to a free fallback
65
+ * to fulfil the request. When `used` is true:
66
+ * - `model` is the model that actually answered (vs `ChatResponse.model`
67
+ * which historically reflected the requested model id).
68
+ * - `settlementSkipped` is `true` when the gateway also skipped the
69
+ * on-chain settle — i.e. the user was not charged for this call
70
+ * because a free fallback served it.
71
+ * Surfaced from the gateway's `X-Fallback-Used / X-Fallback-Model /
72
+ * X-Settlement-Skipped` response headers. Absent when the headers
73
+ * aren't present (most calls).
74
+ */
75
+ fallback?: {
76
+ used: true;
77
+ model?: string;
78
+ settlementSkipped?: boolean;
79
+ };
61
80
  }
62
81
  interface Model {
63
82
  id: string;
@@ -242,6 +261,13 @@ interface ChatOptions {
242
261
  search?: boolean;
243
262
  /** Full Live Search configuration (for search-enabled models) */
244
263
  searchParameters?: SearchParameters;
264
+ /**
265
+ * Models to try in order if the primary returns a transient error
266
+ * (timeout, network, 5xx). 4xx and PaymentError still propagate
267
+ * immediately. `smartChat` populates this from the routing tier's
268
+ * fallback chain automatically.
269
+ */
270
+ fallbackModels?: string[];
245
271
  }
246
272
  interface ChatCompletionOptions {
247
273
  /** Max tokens to generate */
@@ -258,6 +284,12 @@ interface ChatCompletionOptions {
258
284
  tools?: Tool[];
259
285
  /** Tool selection strategy */
260
286
  toolChoice?: ToolChoice;
287
+ /**
288
+ * Models to try in order if the primary returns a transient error
289
+ * (timeout, network, 5xx). 4xx and PaymentError still propagate
290
+ * immediately.
291
+ */
292
+ fallbackModels?: string[];
261
293
  }
262
294
  type RoutingProfile = "free" | "eco" | "auto" | "premium";
263
295
  type RoutingTier = "SIMPLE" | "MEDIUM" | "COMPLEX" | "REASONING";
@@ -270,6 +302,12 @@ interface RoutingDecision {
270
302
  costEstimate: number;
271
303
  baselineCost: number;
272
304
  savings: number;
305
+ /**
306
+ * Remaining tier models with known pricing, in fallback order. `chat()`
307
+ * walks this list when the primary model hits a transient error
308
+ * (timeout, network, 5xx). Excludes the primary itself.
309
+ */
310
+ fallbacks?: string[];
273
311
  }
274
312
  interface SmartChatOptions extends ChatOptions {
275
313
  /** Routing profile: free (zero cost), eco (budget), auto (balanced), premium (best quality) */
@@ -689,29 +727,11 @@ declare class APIError extends BlockrunError {
689
727
  * BlockRun LLM Gateway Client.
690
728
  *
691
729
  * Provides access to multiple LLM providers (OpenAI, Anthropic, Google, etc.)
692
- * with automatic x402 micropayments on Base chain.
693
- *
694
- * Networks:
695
- * - Mainnet: https://blockrun.ai/api (Base, Chain ID 8453)
696
- * - Testnet: https://testnet.blockrun.ai/api (Base Sepolia, Chain ID 84532)
697
- *
698
- * @example Testnet usage
699
- * ```ts
700
- * // Use testnet convenience function
701
- * import { testnetClient } from '@blockrun/llm';
702
- * const client = testnetClient({ privateKey: '0x...' });
703
- * const response = await client.chat('openai/gpt-oss-20b', 'Hello!');
704
- *
705
- * // Or configure manually
706
- * const client = new LLMClient({
707
- * privateKey: '0x...',
708
- * apiUrl: 'https://testnet.blockrun.ai/api'
709
- * });
710
- * ```
730
+ * with automatic x402 micropayments on Base chain (Mainnet, Chain ID 8453).
731
+ * API base: https://blockrun.ai/api
711
732
  */
712
733
  declare class LLMClient {
713
734
  static readonly DEFAULT_API_URL = "https://blockrun.ai/api";
714
- static readonly TESTNET_API_URL = "https://testnet.blockrun.ai/api";
715
735
  private account;
716
736
  private privateKey;
717
737
  private apiUrl;
@@ -779,17 +799,48 @@ declare class LLMClient {
779
799
  private getModelPricing;
780
800
  /**
781
801
  * Fetch model pricing from API.
802
+ *
803
+ * For flat-billed models (e.g. ZAI GLM-5 family at $0.001/call) the
804
+ * router still expects per-token rates, so we synthesise an equivalent
805
+ * per-token price assuming ~1500 total tokens per call. Without this,
806
+ * flat models would resolve to inputPrice=outputPrice=0 and the router
807
+ * would treat them as free, biasing routing decisions and reporting
808
+ * inflated savings %.
782
809
  */
783
810
  private fetchModelPricing;
784
811
  /**
785
812
  * Full chat completion interface (OpenAI-compatible).
786
813
  *
787
- * @param model - Model ID
814
+ * When `fallbackModels` is set, transient failures (timeouts, network
815
+ * errors, 5xx) on the primary model trigger a retry against the next
816
+ * model in the list before raising. 4xx errors and PaymentError
817
+ * propagate immediately — those aren't "swap upstream and retry"
818
+ * situations. Each fallback hop logs one stderr line.
819
+ *
820
+ * @param model - Primary model ID
788
821
  * @param messages - Array of messages with role and content
789
822
  * @param options - Optional completion parameters
790
823
  * @returns ChatResponse object with choices and usage
791
824
  */
792
825
  chatCompletion(model: string, messages: ChatMessage[], options?: ChatCompletionOptions): Promise<ChatResponse>;
826
+ /**
827
+ * Write a canonical cost_log entry after a settled x402 payment.
828
+ * Best-effort: failures here must never break a successful API call.
829
+ * Mirrors what Franklin's AgentClient writes via src/agent/llm.ts so
830
+ * cost_log.jsonl is a single source of truth regardless of caller.
831
+ */
832
+ private recordCost;
833
+ /**
834
+ * Parse the chat response JSON and attach `fallback` metadata when the
835
+ * gateway signalled a transparent free-fallback substitution. The
836
+ * gateway sets X-Fallback-Used / X-Fallback-Model / X-Settlement-Skipped
837
+ * on the response when it served a paid request from a free model
838
+ * (route.ts createPaymentResponseHeader path). Without surfacing these
839
+ * to the caller, the user gets a different model than requested with
840
+ * no visibility — silent quality drop and no clue why the on-chain
841
+ * balance didn't change.
842
+ */
843
+ private parseChatResponse;
793
844
  /**
794
845
  * Make a request with automatic x402 payment handling.
795
846
  */
@@ -836,29 +887,37 @@ declare class LLMClient {
836
887
  */
837
888
  private fetchWithTimeout;
838
889
  /**
839
- * List available LLM models with pricing.
890
+ * List available models with pricing.
891
+ *
892
+ * Returns the full `/v1/models` unified catalog (chat + image + music).
893
+ * The shape preserves backwards compatibility — image/music rows have
894
+ * `inputPrice = outputPrice = 0` since those fields don't apply, and
895
+ * their per-call price surfaces via `flatPrice`.
840
896
  */
841
897
  listModels(): Promise<Model[]>;
842
898
  /**
843
899
  * List available image generation models with pricing.
900
+ *
901
+ * The dedicated `/v1/images/models` endpoint was deprecated server-side;
902
+ * image models live in the unified `/v1/models` catalog under
903
+ * `categories: ["image", ...]`. This method filters that catalog so
904
+ * existing callers keep working.
844
905
  */
845
906
  listImageModels(): Promise<ImageModel[]>;
846
907
  /**
847
- * List all available models (both LLM and image) with pricing.
908
+ * List all available models (chat, image, music, etc.) with pricing.
848
909
  *
849
- * @returns Array of all models with 'type' field ('llm' or 'image')
850
- *
851
- * @example
852
- * const models = await client.listAllModels();
853
- * for (const model of models) {
854
- * if (model.type === 'llm') {
855
- * console.log(`LLM: ${model.id} - $${model.inputPrice}/M input`);
856
- * } else {
857
- * console.log(`Image: ${model.id} - $${model.pricePerImage}/image`);
858
- * }
859
- * }
910
+ * @returns Array of all models with `type` field set from category
911
+ * (`llm` for chat, `image` / `music` for media). Backwards-compat:
912
+ * chat models always report `type: "llm"`.
860
913
  */
861
914
  listAllModels(): Promise<(Model | ImageModel)[]>;
915
+ /**
916
+ * Internal: fetch the raw `/v1/models` catalog without normalising shape.
917
+ * Used by listImageModels / listAllModels so each can pick category-
918
+ * specific fields.
919
+ */
920
+ private fetchRawModels;
862
921
  /**
863
922
  * Edit an image using img2img.
864
923
  *
@@ -876,6 +935,17 @@ declare class LLMClient {
876
935
  * @returns SearchResult with summary and citations
877
936
  */
878
937
  search(query: string, options?: SearchOptions): Promise<SearchResult>;
938
+ /**
939
+ * Generic Exa endpoint proxy (POST). Useful when you need an Exa API
940
+ * surface that the typed wrappers below don't expose.
941
+ *
942
+ * @param path - Exa endpoint segment: "search" | "find-similar" | "contents" | "answer"
943
+ * @param body - Request body (see Exa API docs)
944
+ *
945
+ * @example
946
+ * const results = await client.exa("search", { query: "latest AI research", numResults: 5 });
947
+ */
948
+ exa(path: string, body: Record<string, unknown>): Promise<Record<string, unknown>>;
879
949
  /**
880
950
  * Neural web search via Exa. Returns semantically relevant URLs and metadata.
881
951
  * Understands meaning, not just keywords. $0.01/call.
@@ -907,9 +977,7 @@ declare class LLMClient {
907
977
  */
908
978
  exaFindSimilar(url: string, options?: ExaFindSimilarOptions): Promise<ExaSearchResponse>;
909
979
  /**
910
- * Get USDC balance on Base network.
911
- *
912
- * Automatically detects mainnet vs testnet based on API URL.
980
+ * Get USDC balance on Base mainnet.
913
981
  *
914
982
  * @returns USDC balance as a float (6 decimal places normalized)
915
983
  *
@@ -1120,38 +1188,7 @@ declare class LLMClient {
1120
1188
  * Get the wallet address being used for payments.
1121
1189
  */
1122
1190
  getWalletAddress(): string;
1123
- /**
1124
- * Check if client is configured for testnet.
1125
- */
1126
- isTestnet(): boolean;
1127
1191
  }
1128
- /**
1129
- * Create a testnet LLM client for development and testing.
1130
- *
1131
- * This is a convenience function that creates an LLMClient configured
1132
- * for the BlockRun testnet (Base Sepolia).
1133
- *
1134
- * @param options - Client options (privateKey required unless BASE_CHAIN_WALLET_KEY env var is set)
1135
- * @returns LLMClient configured for testnet
1136
- *
1137
- * @example
1138
- * ```ts
1139
- * import { testnetClient } from '@blockrun/llm';
1140
- *
1141
- * const client = testnetClient({ privateKey: '0x...' });
1142
- * const response = await client.chat('openai/gpt-oss-20b', 'Hello!');
1143
- * ```
1144
- *
1145
- * Testnet Setup:
1146
- * 1. Get testnet ETH from https://www.alchemy.com/faucets/base-sepolia
1147
- * 2. Get testnet USDC from https://faucet.circle.com/
1148
- * 3. Use your wallet with testnet funds
1149
- *
1150
- * Available Testnet Models:
1151
- * - openai/gpt-oss-20b
1152
- * - openai/gpt-oss-120b
1153
- */
1154
- declare function testnetClient(options?: Omit<LLMClientOptions, 'apiUrl'>): LLMClient;
1155
1192
 
1156
1193
  /**
1157
1194
  * BlockRun Image Client - Generate images via x402 micropayments.
@@ -1216,6 +1253,11 @@ declare class ImageClient {
1216
1253
  edit(prompt: string, image: string, options?: ImageEditOptions): Promise<ImageResponse>;
1217
1254
  /**
1218
1255
  * List available image generation models with pricing.
1256
+ *
1257
+ * The dedicated `/v1/images/models` endpoint was deprecated server-side;
1258
+ * image models live in the unified `/v1/models` catalog under
1259
+ * `categories: ["image", ...]`. This method filters that catalog so
1260
+ * existing callers keep working.
1219
1261
  */
1220
1262
  listImageModels(): Promise<ImageModel[]>;
1221
1263
  /**
@@ -1850,18 +1892,29 @@ declare function status(): Promise<{
1850
1892
  balance: number;
1851
1893
  }>;
1852
1894
 
1895
+ /** Canonical on-wire schema for cost_log.jsonl entries. */
1853
1896
  interface CostEntry {
1854
- timestamp: string;
1855
- model: string;
1856
- inputTokens: number;
1857
- outputTokens: number;
1858
- costUsd: number;
1897
+ /** Unix epoch seconds (float, millisecond precision). */
1898
+ ts: number;
1899
+ /** API endpoint path, e.g. "/v1/chat/completions". */
1900
+ endpoint: string;
1901
+ /** Settled USDC amount (USD, 6-decimal precision). */
1902
+ cost_usd: number;
1903
+ /** Model id when known, e.g. "zai/glm-5-turbo". Optional for non-LLM endpoints. */
1904
+ model?: string;
1905
+ /** Payer wallet address (EVM 0x... or Solana base58). */
1906
+ wallet?: string;
1907
+ /** Network identifier — "eip155:8453" for Base mainnet, "solana-mainnet", etc. */
1908
+ network?: string;
1909
+ /** Caller kind for analytics — "LLMClient", "ImageClient", "AgentClient", ... */
1910
+ client_kind?: string;
1859
1911
  }
1860
1912
  declare function logCost(entry: CostEntry): void;
1861
1913
  declare function getCostSummary(): {
1862
1914
  totalUsd: number;
1863
1915
  calls: number;
1864
1916
  byModel: Record<string, number>;
1917
+ byEndpoint: Record<string, number>;
1865
1918
  };
1866
1919
 
1867
1920
  /**
@@ -2077,4 +2130,4 @@ declare function validateTemperature(temperature?: number): void;
2077
2130
  */
2078
2131
  declare function validateTopP(topP?: number): void;
2079
2132
 
2080
- export { APIError, AnthropicClient, type AudioModel, type AudioTrack, BASE_CHAIN_ID, type BarResolution, type BlockRunAnthropicOptions, BlockrunError, type ChatChoice, type ChatCompletionOptions, type ChatMessage, type ChatOptions, type ChatResponse, type ChatResponseWithCost, type ChatUsage, type CostEntry, type CostEstimate, type CreatePaymentOptions, type FunctionCall, type FunctionDefinition, type HistoryOptions, ImageClient, type ImageClientOptions, type ImageData, type ImageEditOptions, type ImageGenerateOptions, type ImageModel, type ImageResponse, KNOWN_PROVIDERS, LLMClient, type LLMClientOptions, type ListOptions, type MarketSession, type Model, MusicClient, type MusicClientOptions, type MusicGenerateOptions, type MusicResponse, type NewsSearchSource, OpenAI, type OpenAIChatCompletionChoice, type OpenAIChatCompletionChunk, type OpenAIChatCompletionParams, type OpenAIChatCompletionResponse, type OpenAIClientOptions, PaymentError, type PaymentLinks, type PriceBar, type PriceCategory, PriceClient, type PriceClientOptions, type PriceHistoryResponse, type PriceOptions, type PricePoint, type RoutingDecision, type RoutingProfile, type RoutingTier, type RssSearchSource, SOLANA_NETWORK, SOLANA_WALLET_FILE as SOLANA_WALLET_FILE_PATH, SearchClient, type SearchClientOptions, type SearchOptions, type SearchParameters, type SearchResult, type SearchSource, type SearchUsage, type SmartChatOptions, type SmartChatResponse, SolanaLLMClient, type SolanaLLMClientOptions, type SolanaWalletInfo, type Spending, type SpendingReport, type StockMarket, type SymbolListResponse, type Tool, type ToolCall, type ToolChoice, USDC_BASE, USDC_BASE_CONTRACT, USDC_SOLANA, type VideoClientOptions, type VideoClip, type VideoGenerateOptions, type VideoModel, type VideoResponse, WALLET_DIR_PATH, WALLET_FILE_PATH, type WalletInfo, type WebSearchSource, type XArticlesRisingResponse, type XAuthorAnalyticsResponse, XClient, type XClientOptions, type XCompareAuthorsResponse, type XFollower, type XFollowersResponse, type XFollowingsResponse, type XMentionsOptions, type XMentionsResponse, type XSearchOptions, type XSearchResponse, type XSearchSource, type XTrendingResponse, type XTweet, type XTweetLookupResponse, type XTweetRepliesOptions, type XTweetRepliesResponse, type XTweetThreadResponse, type XTweetsResponse, type XUser, type XUserInfoResponse, type XUserLookupResponse, type XUserTweetsOptions, type XVerifiedFollowersResponse, clearCache, createPaymentPayload, createSolanaPaymentPayload, createSolanaWallet, createWallet, LLMClient as default, extractPaymentDetails, formatFundingMessageCompact, formatNeedsFundingMessage, formatWalletCreatedMessage, getCached, getCachedByRequest, getCostLogSummary, getCostSummary, getEip681Uri, getOrCreateSolanaWallet, getOrCreateWallet, getPaymentLinks, getWalletAddress, loadSolanaWallet, loadWallet, logCost, parsePaymentRequired, saveSolanaWallet, saveToCache, saveWallet, scanSolanaWallets, scanWallets, setCache, setupAgentSolanaWallet, setupAgentWallet, solanaClient, solanaKeyToBytes, solanaPublicKey, status, testnetClient, validateMaxTokens, validateModel, validateTemperature, validateTopP };
2133
+ export { APIError, AnthropicClient, type AudioModel, type AudioTrack, BASE_CHAIN_ID, type BarResolution, type BlockRunAnthropicOptions, BlockrunError, type ChatChoice, type ChatCompletionOptions, type ChatMessage, type ChatOptions, type ChatResponse, type ChatResponseWithCost, type ChatUsage, type CostEntry, type CostEstimate, type CreatePaymentOptions, type FunctionCall, type FunctionDefinition, type HistoryOptions, ImageClient, type ImageClientOptions, type ImageData, type ImageEditOptions, type ImageGenerateOptions, type ImageModel, type ImageResponse, KNOWN_PROVIDERS, LLMClient, type LLMClientOptions, type ListOptions, type MarketSession, type Model, MusicClient, type MusicClientOptions, type MusicGenerateOptions, type MusicResponse, type NewsSearchSource, OpenAI, type OpenAIChatCompletionChoice, type OpenAIChatCompletionChunk, type OpenAIChatCompletionParams, type OpenAIChatCompletionResponse, type OpenAIClientOptions, PaymentError, type PaymentLinks, type PriceBar, type PriceCategory, PriceClient, type PriceClientOptions, type PriceHistoryResponse, type PriceOptions, type PricePoint, type RoutingDecision, type RoutingProfile, type RoutingTier, type RssSearchSource, SOLANA_NETWORK, SOLANA_WALLET_FILE as SOLANA_WALLET_FILE_PATH, SearchClient, type SearchClientOptions, type SearchOptions, type SearchParameters, type SearchResult, type SearchSource, type SearchUsage, type SmartChatOptions, type SmartChatResponse, SolanaLLMClient, type SolanaLLMClientOptions, type SolanaWalletInfo, type Spending, type SpendingReport, type StockMarket, type SymbolListResponse, type Tool, type ToolCall, type ToolChoice, USDC_BASE, USDC_BASE_CONTRACT, USDC_SOLANA, type VideoClientOptions, type VideoClip, type VideoGenerateOptions, type VideoModel, type VideoResponse, WALLET_DIR_PATH, WALLET_FILE_PATH, type WalletInfo, type WebSearchSource, type XArticlesRisingResponse, type XAuthorAnalyticsResponse, XClient, type XClientOptions, type XCompareAuthorsResponse, type XFollower, type XFollowersResponse, type XFollowingsResponse, type XMentionsOptions, type XMentionsResponse, type XSearchOptions, type XSearchResponse, type XSearchSource, type XTrendingResponse, type XTweet, type XTweetLookupResponse, type XTweetRepliesOptions, type XTweetRepliesResponse, type XTweetThreadResponse, type XTweetsResponse, type XUser, type XUserInfoResponse, type XUserLookupResponse, type XUserTweetsOptions, type XVerifiedFollowersResponse, clearCache, createPaymentPayload, createSolanaPaymentPayload, createSolanaWallet, createWallet, LLMClient as default, extractPaymentDetails, formatFundingMessageCompact, formatNeedsFundingMessage, formatWalletCreatedMessage, getCached, getCachedByRequest, getCostLogSummary, getCostSummary, getEip681Uri, getOrCreateSolanaWallet, getOrCreateWallet, getPaymentLinks, getWalletAddress, loadSolanaWallet, loadWallet, logCost, parsePaymentRequired, saveSolanaWallet, saveToCache, saveWallet, scanSolanaWallets, scanWallets, setCache, setupAgentSolanaWallet, setupAgentWallet, solanaClient, solanaKeyToBytes, solanaPublicKey, status, validateMaxTokens, validateModel, validateTemperature, validateTopP };