tokenlens
Migrations

Migration: v1 → v2

Map v1 helpers and patterns to Tokenlens v2 with step-by-step replacements.

Tokenlens v2 replaces the all-in-one helper bundle with a focused client that loads real-time provider data, handles caching, and exposes a minimal helper surface. This guide maps each common v1 pattern to its v2 counterpart, explains the rationale, and lists the precise migration steps.

Quick Checklist

  • Replace legacy helpers with getModelData, computeCostUSD, and getContextLimits (either via dedicated Tokenlens instances or top-level helpers that reuse the shared instance internally).
  • Swap static catalog imports for your own Tokenlens instance when you need a specific catalog or fixture data (the default auto catalog now resolves to OpenRouter).
  • Remove conversation/compaction helpers (shouldCompact, contextHealth, etc.) from production code; re-implement app-specific logic on top of getModelData+computeCostUSD.
  • Delete usages of registry globals (MODEL_IDS, resolveModel, etc.); rely on tokenlens.refresh() (or your fixture catalog) if you truly need the raw provider map.
  • Update docs/README snippets to use the new API names and note that omitting catalog (or using catalog: "auto") selects the OpenRouter gateway.

The Tokenlens class at a glance

  • Single responsibility: load provider data from a selected catalog (defaults to the OpenRouter gateway, including when catalog: "auto" is provided) and expose consistent helpers (getModelData, computeCostUSD, getContextLimits, getContextHealth, etc.).
  • Lazy shared instance: module-level helpers create a default instance on first use; without options they use the OpenRouter catalog (the same data returned by { catalog: "auto" }), so override with another id when needed.
  • Custom instances: createTokenlens(options) lets you choose a catalog (gateway id or SourceProviders), tweak TTL, or plug in your own cache adapter.
  • Caching: by default, provider catalogs are cached in-memory for 24 hours with jitter. Control this via ttlMs or a custom cache. Errors fall back to the last cached value when possible.
  • Flexible catalogs: switch gateways on a per-instance basis with catalog, or override on individual helper calls via the gateway option.

See the README for end-to-end examples of both standalone helpers and configured instances.

What Changed and Why

Areav1 patternv2 replacementWhy it changed / Benefit
API surfaceRoot module re-exported most scoped packages plus legacy sync helpers.Root module exports Tokenlens, createTokenlens, getModelData, computeCostUSD, getContextLimits, and type-only exports (Usage, ModelDetails, TokenCosts, TokenlensOptions, etc.).Smaller surface, clear entry point, no accidental coupling to internals.
Source dataBundled static catalog (defaultCatalog, registry wrappers).Catalogs are selected via the catalog option (auto, openrouter, models.dev, vercel, or a custom SourceProviders).Avoids stale data, shrinks bundle size, lets apps decide which datasets to load.
Cost/context helpersSync helpers that implicitly read the bundled catalog (getContext, estimateCost, remainingContext, shouldCompact, etc.).Use getModelData, getContextLimits, computeCostUSD; implement conversation heuristics yourself.Ensures helpers operate on the same resolved metadata and encourages app-specific policies.
Registry helpersGlobal listings (MODEL_IDS, resolveModel, listModels).Pull a fresh catalog via tokenlens.refresh() or source it from @tokenlens/core if you need the raw DTOs.Keeps v2 runtime lean and makes registry logic explicit.
Fetch utilitiesDedicated fetchModels helper.Call tokenlens.refresh() (loads the current catalog) or instantiate createTokenlens({ catalog: ... }).One code path handles loading, caching, retries.

Side-by-Side Imports

// v1 usage
import {
  getContext,
  estimateCost,
  remainingContext,
  fetchModels,
  MODEL_IDS,
} from "tokenlens";

// v2 usage
import {
  createTokenlens,
  getModelData,
  computeCostUSD,
  getContextLimits,
} from "tokenlens";

// optional singleton if you don't need custom loaders
const tokenlens = createTokenlens();
const details = await tokenlens.getModelData({ modelId: "openai/gpt-4o" });
const cost = await tokenlens.computeCostUSD({ modelId: "openai/gpt-4o", usage });
const context = await tokenlens.getContextLimits({ modelId: "openai/gpt-4o" });

Migration Steps

1. Instantiate or reuse the client

Old

// Helpers implicitly used the bundled catalog
import { getContext, estimateCost } from "tokenlens";
const context = getContext("openai/gpt-4o");
const cost = estimateCost({ modelId: "openai/gpt-4o", usage });

New

import { createTokenlens } from "tokenlens";

const tokenlens = createTokenlens();
const details = await tokenlens.getModelData({ modelId: "openai/gpt-4o" });
const context = await tokenlens.getContextLimits({ modelId: "openai/gpt-4o" });
const cost = await tokenlens.computeCostUSD({ modelId: "openai/gpt-4o", usage });

Why: The client keeps provider catalogs in sync, enforces loader availability, and caches responses so all helper calls share the same data. If you don't need custom options and are comfortable with defaults, the module-level helpers will reuse a shared instance automatically.

2. Replace catalog-dependent helpers

Focus on re-implementing only the app-specific bits you truly need. getModelData exposes model metadata, while computeCostUSD gives request-level costs.

Old

import { remainingContext, shouldCompact } from "tokenlens";
const { remainingCombined, percentUsed } = remainingContext({ modelId, usage });
const compact = shouldCompact({ modelId, usage, threshold: 0.85 });

New

const model = await tokenlens.getModelData({ modelId });
const costs = await tokenlens.computeCostUSD({ modelId, usage });
const totalTokens = usage.prompt_tokens! + usage.completion_tokens!;
const remainingCombined = model?.limit?.context
  ? Math.max(0, model.limit.context - totalTokens)
  : undefined;
const percentUsed = model?.limit?.context
  ? totalTokens / model.limit.context
  : 1;
const compact = percentUsed >= 0.85;
const totalUsd = costs.totalTokenCostUSD;

Why: v2 intentionally drops opinionated compaction/budgeting helpers. Consumers own the heuristics; Tokenlens supplies accurate metadata and costs.

3. Drop registry globals

Old

import { MODEL_IDS, resolveModel } from "tokenlens";
const alts = MODEL_IDS.filter((id) => id.startsWith("openai:"));
const model = resolveModel("gpt-4o");

New

const providers = await tokenlens.refresh();
const model = providers.openai?.models?.["openai/gpt-4o"];
const allIds = Object.values(providers)
  .flatMap((p) => Object.keys(p.models ?? {}));

Why: Removing the bundled registry keeps installs small and guarantees you work with the same catalog that powered your helper calls.

4. Swap asynchronous fetch helpers

Old

import { fetchModels } from "tokenlens";
const catalog = await fetchModels();

New

const providers = await tokenlens.refresh(); // defaults to the OpenRouter catalog
// or configure: createTokenlens({ catalog: "openrouter" }) / createTokenlens({ catalog: "models.dev" })

5. Recreate conversation/cost rollups as needed

Helpers such as estimateConversationCost, nextTurnBudget, contextHealth, tokensRemaining, and shouldCompact were frequently tailored downstream. With v2:

  • Use tokenlens.getModelData({ modelId }) for metadata and tokenlens.computeCostUSD({ modelId, usage }) for runtime costs.
  • Aggregate usage metrics in your domain objects.
  • Apply your own heuristics for compaction, leak warnings, or budgeting.

Tokenlens no longer ships replacements for these helpers. This is intentional: they deferred too many policy decisions to the library and became hard to maintain.

Benefits Recap

  • Smaller install & clearer API: No more accidental pulls of scoped packages or large static catalogs.
  • Explicit data ownership: Apps decide which catalog loads and when it refreshes, reducing surprise updates.
  • Consistent helper behaviour: getModelData, computeCostUSD, and getContextLimits all operate on the same cached provider data.
  • Easier customization: With opinionated conversation helpers removed, you can tailor budgeting, compaction, and reporting to your product.

FAQ

Do I need to import other packages now? No. Creating a client, inspecting models, and estimating costs are all exposed directly from tokenlens v2. Additional DTO types (Usage, ModelDetails, TokenlensOptions, etc.) are re-exported for convenience.

Can I keep a singleton? Yes. The module helpers (computeCostUSD, etc.) reuse a shared client internally. If you need custom options, create your own createTokenlens() factory and reuse that instance.

How do I seed tests without hitting the network? Pass a SourceProviders fixture via createTokenlens({ catalog: fixtureProviders, ttlMs: 0 }).


If anything is missing or unclear, please open an issue so we can refine this guide.