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

opengradient-ai-provider

v0.1.1

Published

Vercel AI SDK provider for OpenGradient TEE LLM (verifiable inference)

Readme

opengradient-ai-provider

A Vercel AI SDK community provider for the OpenGradient TEE LLM: verifiable inference inside a Trusted Execution Environment, paid for on-chain via x402.

Implements LanguageModelV3: generateText, streamText, and tool calling, with TEE attestation surfaced through providerMetadata.

Install

npm install opengradient-ai-provider ai

ai (the Vercel AI SDK) is a peer dependency.

Quick start

import { createOpenGradient } from 'opengradient-ai-provider';
import { generateText } from 'ai';

const opengradient = createOpenGradient({
  // server-only; falls back to OPENGRADIENT_PRIVATE_KEY when omitted
  privateKey: process.env.OPENGRADIENT_PRIVATE_KEY,
  // see "TEE endpoints" below, currently required while the SDK registry is stale
  llmServerUrl: process.env.OPENGRADIENT_LLM_SERVER_URL?.split(','),
});

const { text, providerMetadata } = await generateText({
  model: opengradient('anthropic/claude-haiku-4-5'),
  prompt: 'In one sentence, what is a TEE?',
});

console.log(text);
console.log('TEE signature:', providerMetadata?.opengradient?.teeSignature);

TEE endpoints (llmServerUrl): currently required

The published OpenGradient SDK ships a default on-chain TEE registry that currently returns no active TEEs, so the normal discovery path fails. As an interim workaround, pass one or more TEE endpoints explicitly via llmServerUrl (a string or an array). The provider tries them in order and fails over to the next on a connection failure, surfacing a warning when it does.

const opengradient = createOpenGradient({
  llmServerUrl: ['https://13.59.207.188', 'https://3.15.214.21'],
});

Caveats: passing llmServerUrl bypasses on-chain TLS pinning, and the endpoint IPs rotate over time. This is temporary until the SDK's registry discovery is fixed (after which no llmServerUrl is needed). You can also set OPENGRADIENT_LLM_SERVER_URL (comma-separated for a failover list).

Streaming

import { streamText } from 'ai';

const result = streamText({
  model: opengradient('anthropic/claude-haiku-4-5'),
  prompt: 'Explain verifiable inference in two sentences.',
});

for await (const delta of result.textStream) process.stdout.write(delta);
console.log('\n', await result.providerMetadata);

Tool calling

import { generateText, tool, jsonSchema } from 'ai';

const { toolCalls, finishReason } = await generateText({
  model: opengradient('anthropic/claude-haiku-4-5'),
  prompt: 'What is the weather in Paris? Use the tool.',
  tools: {
    get_weather: tool({
      description: 'Get the current weather for a city.',
      inputSchema: jsonSchema<{ city: string }>({
        type: 'object',
        properties: { city: { type: 'string' } },
        required: ['city'],
      }),
    }),
  },
});

streamText with tools works too, but is degraded upstream: the SDK falls back to non-streaming and returns the tool call in a single final chunk (arguments are not token-streamed). The provider synthesizes the proper V3 tool-call stream parts from that chunk.

TEE attestation

Every response exposes attestation and payment data under providerMetadata.opengradient:

| Field | Description | | ------------------------------------------------------- | ------------------------------------------------------------ | | teeSignature | RSA-PSS signature over the response (verifiable). | | teeId | On-chain registry id of the enclave that served the request. | | teeTimestamp | ISO-8601 signing time. | | teeEndpoint | Endpoint URL of the serving TEE. | | teePaymentAddress | Payment address registered for the TEE. | | paymentHash | x402 payment hash (non-streaming only). | | dataSettlementTransactionHash, dataSettlementBlobId | Data-settlement details, when available. |

Configuration

createOpenGradient(settings): all fields optional, each has an env fallback.

| Setting | Type | Env fallback | Notes | | -------------------- | -------------------- | ----------------------------------------------- | ----------------------------------------------------------------------------------------- | | privateKey | string | OPENGRADIENT_PRIVATE_KEY | EVM key that pays for inference. Server-only. | | rpcUrl | string | OPENGRADIENT_RPC_URL | RPC for the on-chain TEE registry (see "Networks & RPC"). | | llmServerUrl | string \| string[] | OPENGRADIENT_LLM_SERVER_URL (comma-separated) | Explicit TEE endpoint(s) with failover (see above). | | maxPaymentValue | bigint | OPENGRADIENT_MAX_PAYMENT_VALUE | Passed to the SDK. Not enforced as a spend cap upstream, so do not rely on it as one. | | teeRegistryAddress | string | OPENGRADIENT_TEE_REGISTRY_ADDRESS | Override the TEERegistry contract. |

Per-call options

await generateText({
  model: opengradient('anthropic/claude-haiku-4-5'),
  prompt: '...',
  providerOptions: {
    opengradient: {
      // x402 settlement mode: 'private' | 'batch' | 'individual'
      settlementMode: 'individual',
    },
  },
});

Networks & RPC

Two separate networks are involved, plus the TEE servers that run the inference:

| Network | Role | Custom RPC | | -------------------- | ------------------------------------------------------- | -------------------------------------------------------------------- | | Base mainnet | Payment: the OPG token, x402, and Permit2 all live here | BASE_MAINNET_RPC env var (read by the SDK) | | OpenGradient EVM | The on-chain TEE registry used to discover enclaves | rpcUrl setting / OPENGRADIENT_RPC_URL, plus teeRegistryAddress | | TEE servers (HTTP) | The inference itself | llmServerUrl (see above) |

A few things worth knowing:

  • The registry RPC is fully configurable through rpcUrl / teeRegistryAddress. When you use the llmServerUrl workaround, the registry network is skipped entirely.
  • The Base RPC can only be changed through the BASE_MAINNET_RPC environment variable. The SDK reads it directly and does not accept it through provider settings, so there is no provider-level baseRpcUrl option. checkOpenGradientSetup takes its own baseRpcUrl for the Base reads it performs.
  • Mainnet only, by OpenGradient's design. The OPG token and the x402 payment rails are deployed on Base mainnet and hardcoded in the SDK (BASE_OPG_ADDRESS is a fixed Base address). There is no testnet OPG or testnet payment path, so this provider cannot run on a testnet until OpenGradient ships one. The provider is a thin wrapper over the SDK and cannot move the payment chain.

Security: server-only

This provider takes an EVM private key that controls real funds. Run it server-side only (route handler, server action, backend). Never bundle it into client-side code, never hard-code or commit the key, and load it from OPENGRADIENT_PRIVATE_KEY.

Prerequisite: OPG / Permit2 approval

The paying wallet must hold OPG on Base mainnet (and a little ETH for the one-time approval gas), and grant Permit2 approval once before any inference call. The provider intentionally never does this, since it sends on-chain transactions, so you run it yourself:

import { ensureOpgApproval } from 'opengradient-sdk';
import { privateKeyToAccount } from 'viem/accounts';

const account = privateKeyToAccount(
  process.env.OPENGRADIENT_PRIVATE_KEY as `0x${string}`,
);

// run once: approve up to 100 OPG for Permit2 (sends a tx; needs ETH for gas)
await ensureOpgApproval(account, 5, 100);

Preflight: check your setup (read-only)

checkOpenGradientSetup inspects the wallet's OPG balance, ETH-for-gas, and Permit2 allowance on Base so you can fix funding/approval before paying. It sends no transactions:

import { checkOpenGradientSetup } from 'opengradient-ai-provider';
import { privateKeyToAccount } from 'viem/accounts';

const account = privateKeyToAccount(
  process.env.OPENGRADIENT_PRIVATE_KEY as `0x${string}`,
);

const report = await checkOpenGradientSetup(account);
if (!report.ready) {
  console.error(report.issues.join('\n'));
  // e.g. "Permit2 allowance for OPG is 0 OPG. Run ensureOpgApproval(account, 5, 100)..."
}

It accepts a viem Account or a plain address, plus optional { baseRpcUrl } for a custom Base RPC and { minAllowance } in raw atomic units. Inference errors are likewise diagnostic: a 402 tells you to check OPG funds and allowance, not just that payment failed.

Limitations

  • No multimodal: file / image / audio parts are dropped with a warning (text only).
  • Streaming + tools is degraded: the tool call arrives in one synthesized final chunk, not token-streamed, and the upstream SDK omits token usage for this path, so the finish part reports no usage.
  • toolChoice: { type: 'tool' } (force a specific tool) is unsupported; it falls back to 'auto' with a warning.
  • Ignored sampling params: topP, topK, presencePenalty, frequencyPenalty, seed, abortSignal (mid-flight), and headers are not supported and warn.
  • JSON without a schema: responseFormat: { type: 'json' } without a schema maps to json_object, which Anthropic models reject, so provide a schema.
  • OPG / Permit2 approval is a prerequisite (see above).

Examples

Runnable scripts live in examples/ on GitHub (generate-text.ts, stream-text.ts, tool-call.ts).

License

MIT © Gilberts Ahumada