npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ottorouter/ai-sdk

v0.2.2

Published

A drop-in SDK for accessing AI models (OpenAI, Anthropic, Google, Moonshot, MiniMax, Z.AI) through the [OttoRouter](https://github.com/slashforge/ottorouter) proxy with automatic x402 payments via Solana USDC.

Downloads

726

Readme

@ottorouter/ai-sdk

A drop-in SDK for accessing AI models (OpenAI, Anthropic, Google, Moonshot, MiniMax, Z.AI) through the OttoRouter proxy with automatic x402 payments via Solana USDC.

All you need is a Solana wallet — the SDK handles authentication, payment negotiation, and provider routing automatically.

Normal API requests use bearer auth. The SDK signs a wallet nonce once to exchange for a short-lived OttoRouter token, reuses that token across requests, and refreshes it automatically when needed.

Install

bun add @ottorouter/ai-sdk ai
# or
npm install @ottorouter/ai-sdk ai

Quick Start

import { createOttoRouter } from "@ottorouter/ai-sdk";
import { generateText } from "ai";

const ottorouter = createOttoRouter({
  auth: { privateKey: process.env.SOLANA_PRIVATE_KEY! },
});

const { text } = await generateText({
  model: ottorouter.model("claude-sonnet-4-20250514"),
  prompt: "Hello!",
});

console.log(text);

The SDK auto-resolves which provider to use based on the model name. It returns ai-sdk compatible model instances that work directly with generateText(), streamText(), etc.

Under the hood, the first protected request exchanges wallet auth headers for a bearer token via POST /v1/auth/wallet-token. Subsequent requests reuse Authorization: Bearer <token> until refresh is needed.

Provider Auto-Resolution

Models are resolved to providers by prefix:

| Prefix | Provider | API Format | | ---------------------------------- | --------- | ----------- | | claude- | Anthropic | Messages | | gpt-, o1, o3, o4, codex- | OpenAI | Responses | | gemini- | Google | Native | | kimi- | Moonshot | OpenAI Chat | | MiniMax- | MiniMax | Messages | | z1- | Z.AI | OpenAI Chat |

ottorouter.model("claude-sonnet-4-20250514"); // → anthropic
ottorouter.model("gpt-4o"); // → openai
ottorouter.model("gemini-2.5-pro"); // → google
ottorouter.model("kimi-k2"); // → moonshot

Explicit Provider

Override auto-resolution when needed:

const model = ottorouter.provider("openai").model("gpt-4o");
const model = ottorouter
  .provider("anthropic", "anthropic-messages")
  .model("claude-sonnet-4-20250514");

Configuration

const ottorouter = createOttoRouter({
  // Required: Solana wallet private key (base58)
  auth: { privateKey: "..." },

  // Optional: OttoRouter API base URL (default: https://api.ottorouter.org)
  baseURL: "https://api.ottorouter.org",

  // Optional: Solana RPC URL (default: https://api.mainnet-beta.solana.com)
  rpcURL: "https://api.mainnet-beta.solana.com",

  // Optional: Payment callbacks
  callbacks: {
    /* see Payment Callbacks */
  },

  // Optional: Cache configuration
  cache: {
    /* see Caching */
  },

  // Optional: Payment options
  payment: {
    /* see Payment Options */
  },

  // Optional: Custom model→provider mappings
  modelMap: {
    "my-custom-model": "openai",
  },

  // Optional: Register custom providers
  providers: [
    {
      id: "my-provider",
      apiFormat: "openai-chat",
      modelPrefix: "myp-",
    },
  ],
});

Payment Callbacks

Monitor and control the payment lifecycle:

Request authentication and payment signing are separate: bearer auth is used for normal OttoRouter HTTP requests, while your wallet still signs the x402 payment transaction during topups.

const ottorouter = createOttoRouter({
  auth: { privateKey: "..." },
  callbacks: {
    // Called when a 402 is received and payment is needed
    onPaymentRequired: (amountUsd, currentBalance) => {
      console.log(`Payment required: $${amountUsd}`);
    },

    // Called when the SDK is signing a transaction
    onPaymentSigning: () => {
      console.log("Signing payment...");
    },

    // Called after successful payment
    onPaymentComplete: ({ amountUsd, newBalance, transactionId }) => {
      console.log(`Paid $${amountUsd}, balance: $${newBalance}`);
    },

    // Called on payment failure
    onPaymentError: (error) => {
      console.error("Payment failed:", error);
    },

    // Called after each request with cost info (streaming & non-streaming)
    onBalanceUpdate: ({
      costUsd,
      balanceRemaining,
      inputTokens,
      outputTokens,
    }) => {
      console.log(`Cost: $${costUsd}, remaining: $${balanceRemaining}`);
    },

    // Optional: interactive approval before payment
    onPaymentApproval: async ({ amountUsd, currentBalance }) => {
      // return 'crypto' to pay, 'fiat' for fiat flow, 'cancel' to abort
      return "crypto";
    },
  },
});

Payment Options

const ottorouter = createOttoRouter({
  auth: { privateKey: "..." },
  payment: {
    // 'auto' (default) — pay automatically
    // 'approval' — call onPaymentApproval before each payment
    topupApprovalMode: "auto",

    // Auto-pay without approval if wallet USDC balance >= threshold
    autoPayThresholdUsd: 5.0,

    // Max retries for a single API request (default: 3)
    maxRequestAttempts: 3,

    // Max total payment attempts per wallet (default: 20)
    maxPaymentAttempts: 20,
  },
});

Caching

Anthropic Cache Control

By default, the SDK automatically injects cache_control: { type: 'ephemeral' } on the first system block and the last message for Anthropic models. This saves ~90% on cached token costs.

// Default: auto caching (1 system + 1 message breakpoint)
createOttoRouter({ auth });

// Disable completely
createOttoRouter({ auth, cache: { anthropicCaching: false } });

// Manual: SDK won't inject cache_control — set it yourself in messages
createOttoRouter({ auth, cache: { anthropicCaching: { strategy: "manual" } } });

// Custom breakpoint count and placement
createOttoRouter({
  auth,
  cache: {
    anthropicCaching: {
      systemBreakpoints: 2, // cache first 2 system blocks
      systemPlacement: "first", // 'first' | 'last' | 'all'
      messageBreakpoints: 3, // cache last 3 messages
      messagePlacement: "last", // 'first' | 'last' | 'all'
    },
  },
});

// Full custom transform
createOttoRouter({
  auth,
  cache: {
    anthropicCaching: {
      strategy: "custom",
      transform: (body) => {
        // modify body however you want
        return body;
      },
    },
  },
});

| Option | Default | Description | | -------------------- | ------------- | ------------------------------------------------- | | strategy | 'auto' | 'auto', 'manual', 'custom', or false | | systemBreakpoints | 1 | Number of system blocks to cache | | messageBreakpoints | 1 | Number of messages to cache | | systemPlacement | 'first' | Which system blocks: 'first', 'last', 'all' | | messagePlacement | 'last' | Which messages: 'first', 'last', 'all' | | cacheType | 'ephemeral' | The cache_control.type value |

OttoRouter Server-Side Caching

Provider-agnostic caching at the OttoRouter proxy layer:

createOttoRouter({
  auth,
  cache: {
    promptCacheKey: "my-session-123",
    promptCacheRetention: "in_memory", // or '24h'
  },
});

OpenAI / Google

  • OpenAI: Automatic server-side prefix caching — no configuration needed
  • Google: Requires pre-uploaded cachedContent at the application level

Balance

// OttoRouter account balance
const balance = await ottorouter.balance();
// { walletAddress, balance, totalSpent, totalTopups, requestCount }

// On-chain USDC balance
const wallet = await ottorouter.walletBalance("mainnet");
// { walletAddress, usdcBalance, network }

// Wallet address
console.log(ottorouter.walletAddress);

Custom Providers

Register providers at init or runtime:

// At init
const ottorouter = createOttoRouter({
  auth,
  providers: [
    { id: "my-provider", apiFormat: "openai-chat", modelPrefix: "myp-" },
  ],
});

// At runtime
ottorouter.registry.register({
  id: "another-provider",
  apiFormat: "anthropic-messages",
  models: ["specific-model-id"],
});

// Map a specific model to a provider
ottorouter.registry.mapModel("some-model", "openai");

API Formats

| Format | Description | Used by | | -------------------- | ------------------------------------ | ------------------ | | openai-responses | OpenAI Responses API | OpenAI | | anthropic-messages | Anthropic Messages API | Anthropic, MiniMax | | openai-chat | OpenAI Chat Completions (compatible) | Moonshot, Z.AI | | google-native | Google GenerativeAI native | Google |

Low-Level: Custom Fetch

Use the x402-aware fetch wrapper directly:

ottorouter.fetch() uses bearer auth for normal requests and automatically refreshes the OttoRouter access token on 401 once before retrying.

const customFetch = ottorouter.fetch();

const response = await customFetch('https://api.ottorouter.org/v1/messages', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ model: 'claude-sonnet-4-20250514', messages: [...] }),
});

Standalone Utilities

import {
  fetchBalance,
  fetchWalletUsdcBalance,
  getPublicKeyFromPrivate,
  addAnthropicCacheControl,
  createOttoRouterFetch,
  createWalletContext,
} from "@ottorouter/ai-sdk";

// Get wallet address from private key
const address = getPublicKeyFromPrivate(privateKey);

// Fetch balance without creating a full OttoRouter instance
const balance = await fetchBalance({ privateKey });

// Fetch on-chain USDC
const usdc = await fetchWalletUsdcBalance({ privateKey }, "mainnet");

// Create a standalone x402-aware fetch
const ottorouterFetch = createOttoRouterFetch({
  wallet: createWalletContext({ privateKey }),
  baseURL: "https://api.ottorouter.org",
});

createWalletContext() remains available for advanced usage. Its wallet headers are now intended for token exchange only; regular API traffic should go through createOttoRouter(), ottorouter.fetch(), createOttoRouterFetch(), or fetchBalance() so bearer auth refresh is handled automatically.

How It Works

  1. You call ottorouter.model('claude-sonnet-4-20250514') — the SDK resolves this to Anthropic
  2. It creates an ai-sdk provider (@ai-sdk/anthropic) pointed at the OttoRouter proxy
  3. A custom fetch wrapper intercepts all requests to:
    • Exchange signed wallet headers for a short-lived bearer token when needed
    • Inject Authorization: Bearer <token> into normal API requests
    • Inject Anthropic cache control (if enabled)
    • Handle 401 by refreshing the bearer token once and retrying
    • Handle 402 responses by signing USDC payments via x402
    • Sniff balance/cost info from SSE stream comments
  4. During topups, the wallet still signs the x402 transaction, but the /v1/topup HTTP request itself uses bearer auth
  5. The OttoRouter proxy verifies the wallet/token, checks balance, forwards to the real provider, and tracks usage

Requirements

  • Solana wallet with USDC (for payments)
  • ai SDK v6+ as a peer dependency
  • Node.js 18+ or Bun

License

MIT