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

@x12i/ai-tools

v3.3.3

Published

AI model catalogs (open-assets JSON), cost calculation, and LLM utilities

Readme

@x12i/ai-tools

TypeScript package for AI model catalogs, runtime cost calculation, and LLM utilities.

Pricing data is loaded from two JSON catalogs on open-assets.x12i.com, with bundled fallbacks shipped in the published package (src/data):

| Catalog | URL | Used when | |---------|-----|-----------| | Direct vendor | models-catalog.json | provider is openai, anthropic, google, … | | OpenRouter | openrouter-models-catalog.json | provider is openrouter, or as fallback |

Remote catalogs are always treated as the latest source. The JSON version field (e.g. "0.1.0") is publisher metadata only — this library does not read or compare it.

v3.0 breaking changes

Aligned with @x12i/ai-profiles v3.3.0.

Accepted model strings

Only two forms are supported everywhere (model, modelUsed, usedModel, CLI --model, invoke config):

  1. profile/choice — e.g. cheap/default, balanced/default, deep/google_deep
  2. Concrete model id — e.g. deepseek-v3.2, deepseek/deepseek-v3.2, gpt-5.5, openai/gpt-5.5, gemini-2.5-flash-lite

Removed (v3)

| Removed | Replacement | |---------|-------------| | ai-profiles shortcuts (standard, cheapest, economy, …) | Full profile/choice keys (balanced/default, cheap/default, …) | | Bare profile names (cheap, balanced) | cheap/default or { profile: "cheap", choice: "default" } | | Project-local aliases (@x12i/ai-tools/aliases, ai-tools alias CLI, AliasRegistry) | Store profile/choice or concrete model ids in config | | Legacy model shorthands (deepseekv32, claude-sonnet, …) | Catalog ids (deepseek-v3.2, deepseek/deepseek-v3.2) | | listAIShortcuts | listAIProfiles / listAIProfileChoices | | applyOpenRouterInvokePolicy | applyOpenRouterInvokeRouting | | ModelProfileUnroutableError.profileAlias | profileKey |

Migration examples

| Before (v2.x) | After (v3) | |---------------|------------| | standard, cheapest | balanced/default, cheap/default | | cheap (bare) | cheap/default | | ai-tools alias set best … | Store concrete id or profile/choice in config | | deepseekv32 | deepseek-v3.2 or deepseek/deepseek-v3.2 | | applyOpenRouterInvokePolicy(...) | applyOpenRouterInvokeRouting(...) |

Dependency: @x12i/ai-profiles ^3.3.0 (minimum 3.3.0).

Invoke model resolution

Host-agnostic pipeline for AI engines that call LLMProviderRouter.invoke(). Pass { provider?, model }; get router-ready { provider, model, allowOpenRouterProxy?, providerProxy? } plus structured diagnostics.

import { resolveInvokeModel } from "@x12i/ai-tools";
// or: import { resolveInvokeModel } from "@x12i/ai-tools/invoke";

const { router, diagnostics, resolution } = await resolveInvokeModel(
  { provider: invokeConfig.provider, model: invokeConfig.model },
  {
    preferOpenRouter: true,
    openRouterApiKey: process.env.OPENROUTER_API_KEY,
    defaultProvider: "openai",
    modelsOnly: false, // set true to reject profile/choice inputs
  },
);

Object.assign(invokeConfig, router);
// diagnostics.profileKey / invokedModelId for logging when routedViaOpenRouter

| Export | Role | |--------|------| | resolveInvokeModel | Full pipeline (catalog → vendor map → OR routing) | | buildInvokeModelResolverOptions | Build ModelResolverOptions with routeViaOpenRouter when preferred | | mapResolutionToRouterConfig | Map ModelResolutionSuccess{ provider, model } | | applyOpenRouterInvokeRouting | Set allowOpenRouterProxy / providerProxy on router config | | normalizeInvokeModel | Idempotent gateway invoke wire shape (from ai-profiles v3.2) | | resolveAndNormalizeInvokeModel | resolveAIProfilenormalizeInvokeModel | | createAiToolsInvokeClient | Shared catalog + cost calculator + routing env | | ModelProfileUnroutableError | Profile/choice input that failed to route |

Responsibility split: ai-tools owns model strings, catalogs, OpenRouter vs direct transport, and cost. The AI engine owns sampling defaults, maxTokens, message templates, router registration, and activity tracking.

Set resolveModels: false for passthrough (no catalog lookup, no resolution errors).

AI profiles (@x12i/ai-profiles v3)

Profile resolution follows the same strict contract as @x12i/ai-profilesinside and outside this repo (configs, graph nodes, AI engines, any service that calls resolveAIProfile).

Two kinds of model strings

| Intent | What you pass | How ai-tools resolves it | |--------|---------------|---------------------------| | Profile (capability tier) | profile/choice only | @x12i/ai-profiles → concrete provider + modelId | | Concrete model (vendor SKU) | gemini-2.5-flash-lite, deepseek/deepseek-v3.2, … | Catalog / profile-choice index |

flowchart TD
  input["model / modelUsed / usedModel string"]
  input --> composite{"profile/choice?"}
  composite -->|yes| profiles["@x12i/ai-profiles\nresolveAIProfile"]
  composite -->|no| concrete["catalog + registry index\n(concrete model SKU)"]
  profiles --> out["provider + modelId + pricing"]
  concrete --> out

Profile keys: profile/choice (required)

Canonical resolve key: {profile}/{choice} with a forward slash /.

"cheap/default"
"balanced/default"
"deep/google_deep"
"local/llama_cpp_gguf"
  • Profile = capability contract (cheap, balanced, deep, json, …) — not a model name.
  • Choice = implementation variant within that profile (default, google_floor, …).

Bare profile names are not accepted (cheap, balanced, deep alone). ai-tools does not infer default for you.

| Valid | Invalid | |-------|---------| | cheap/default | cheap | | deep/default | standard, cheapest (removed shortcuts) | | balanced/default | balanced (without /choice) |

resolveProfileForAsk — split fields allowed

For ask-node / FuncX config, pass either the combined key or explicit fields (still requires a choice):

import { resolveProfileForAsk, TEXT_CATALOG_LANE } from "@x12i/ai-tools";

await resolveProfileForAsk({
  profile: "cheap/default",
  catalogLane: TEXT_CATALOG_LANE,
});

await resolveProfileForAsk({
  profile: "cheap",
  choice: "default",
  catalogLane: TEXT_CATALOG_LANE,
});

catalogLane is required ("text" for most tiers; "image" for vision).

Concrete model names

When the string is a vendor or OpenRouter model id, resolution uses the catalog and the profile registry index:

import { CostCalculator, AiModelsCatalogClient } from "@x12i/ai-tools";

const calculator = new CostCalculator(catalog);

await calculator.calculate({
  tokens: { prompt: 1_000_000, completion: 0, total: 1_000_000 },
  provider: "google",
  modelUsed: "gemini-2.5-flash-lite",
});

await calculator.calculate({
  tokens: { prompt: 1_000_000, completion: 0, total: 1_000_000 },
  provider: "google",
  modelUsed: "cheap/default",
});
const catalog = new AiModelsCatalogClient();

await catalog.resolveModel({ model: "gemini-2.5-flash-lite", provider: "google" });
await catalog.resolveModel({ model: "cheap/default" });
// await catalog.resolveModel({ model: "cheap" }); // not found
// await catalog.resolveModel({ model: "standard" }); // not found (shortcut removed)

Errors and helpers

import {
  requireProfileResolveKey,
  profileResolveKey,
  ProfileResolveKeyRequiredError,
  formatProfileChoiceKey,
  isKnownProfileChoice,
  ensureConcreteModel,
} from "@x12i/ai-tools";

await requireProfileResolveKey("cheap", { choice: undefined }); // throws

await profileResolveKey("gemini-2.5-flash-lite"); // null — concrete SKU
await profileResolveKey("cheap/default"); // "cheap/default"

isKnownProfileChoice("cheap/default"); // true
isKnownProfileChoice("standard"); // false

What to put in configs

| Stored value | Use when | |--------------|----------| | cheap/default, deep/default | Task / graph tier (profile intent) | | gemini-2.5-flash-lite, deepseek/deepseek-v3.2, openai/gpt-5.5 | Explicit model SKU | | ~~cheap~~, ~~standard~~, ~~deepseekv32~~ | Do not use — rejected in v3 |

Re-exports

import {
  resolveAIProfile,
  listAIProfiles,
  listAIProfileChoices,
  resolveAIProfileByTags,
  resolvePreferOpenRouter,
  readPreferOpenRouterFromEnv,
  ensureConcreteModel,
  resolveProfileForAsk,
  TEXT_CATALOG_LANE,
} from "@x12i/ai-tools";
// or
import { resolveProfileForAsk, requireProfileResolveKey } from "@x12i/ai-tools/profiles";

Install

npm install @x12i/ai-tools

Requires Node.js 20+.

Optional peers: @x12i/helpers, openai, better-sqlite3 (toolbox SQLite storage).

Quick start

import { AiModelsCatalogClient, CostCalculator } from "@x12i/ai-tools";

const catalog = new AiModelsCatalogClient();
const calculator = new CostCalculator(catalog);

const result = await calculator.calculate({
  tokens: { prompt: 1000, completion: 500, total: 1500 },
  provider: "openai",
  usedModel: "gpt-5.5-2026-04-23",
});

console.log(result.cost);
console.log(result.resolvedModelId);
console.log(result.usedModel);
console.log(result.usage);

From activity or invoke records:

const result = await calculator.calculateFromRecord(activityDocument);

Catalog loading and cache

Catalogs are fetched on first use and cached in memory for 24 hours. Override TTL with AI_TOOLS_CACHE_TTL_MS. If a fetch fails, bundled src/data/*.json is used automatically.

npx ai-tools catalog refresh
npx ai-tools catalog verify --json
npx ai-tools catalog refresh --bundled-only

| npm script | Command | |------------|---------| | npm run catalog:refresh | Fetch catalogs and warm cache | | npm run catalog:verify | Verify both catalogs load |

Cost calculation

await calculator.calculate({
  tokens: { prompt: 1000, completion: 500, total: 1500 },
  provider: "openrouter",
  usedModel: "deepseek/deepseek-v3.2",
});

Runtime model priority: usedModelmodelUsedmodel.

Profile vs model in usedModel: pass cheap/default for tier-based pricing; pass gemini-2.5-flash-lite (or vendor/slug) for a concrete SKU.

Smart model resolution

ModelNameResolver normalises provider + model input against the cached catalog. Catalog record aliases (metadata in JSON, e.g. deepseek-v3.2deepseek/deepseek-v3.2) are supported; shorthand tokens (deepseekv32, etc.) are not.

import { AiModelsCatalogClient, ModelNameResolver } from "@x12i/ai-tools";

const catalog = new AiModelsCatalogClient();
const models = await catalog.getAllModels();
const resolver = new ModelNameResolver(models);

const result = await catalog.resolveModel({
  provider: "openrouter",
  model: "deepseek-v3.2",
});

| Input | Typical resolution | |-------|-------------------| | openrouter + deepseek-v3.2 | deepseek/deepseek-v3.2 | | cheap/default | ai-profiles → e.g. google/gemini-2.5-flash-lite | | standard, cheapest | not found (use balanced/default, cheap/default) | | deepseekv32 | not found (use deepseek-v3.2) | | gemini-2.5-flash-lite | registry index / catalog | | ollama + llama3:8b | local passthrough |

OpenRouter vs direct routing

| Condition | Routes via OpenRouter | |-----------|----------------------| | PREFER_OPENROUTER true (default) and OPENROUTER_API_KEY set | Yes | | OPENROUTER_API_KEY set, vendor {VENDOR}_API_KEY missing | Yes | | Vendor key present, PREFER_OPENROUTER=false or 0 | Direct |

import {
  loadOpenRouterRoutingEnv,
  shouldDefaultRouteViaOpenRouter,
  resolvePreferOpenRouter,
} from "@x12i/ai-tools";

CLI

npx ai-tools catalog refresh
npx ai-tools catalog verify --json
npx ai-tools models list --provider openai
npx ai-tools models resolve --model deepseek-v3.2 --provider openrouter --verbose
npx ai-tools cost --model openai/gpt-5.5 --prompt-tokens 1000 --completion-tokens 500 --provider openai
npx ai-tools cost --model cheap/default --prompt-tokens 1000000 --completion-tokens 0 --provider google
npx ai-tools models resolve --model cheap/default --verbose

Environment variables

Copy .env.example to .env in your app.

| Variable | Purpose | |----------|---------| | OPENROUTER_API_KEY | OpenRouter routing defaults; enables invoke proxy routing when set | | PREFER_OPENROUTER | true / 1 / false / 0 — prefer OpenRouter when OR + vendor keys both set (default true when unset; parsed via @x12i/ai-profiles) | | OPENAI_API_KEY, ANTHROPIC_API_KEY, … | Direct vendor keys ({PROVIDER_ID}_API_KEY) | | AI_TOOLS_CACHE_TTL_MS | Catalog in-memory cache TTL (default: 86400000 = 24h) |

Package exports

| Subpath | Contents | |---------|----------| | @x12i/ai-tools | Catalog client, cost calculator, resolver, profiles, invoke | | @x12i/ai-tools/cost | Cost types, extractUsageInput, CostCalculator | | @x12i/ai-tools/catalog | loadCatalogSources, refresh/verify | | @x12i/ai-tools/models | Model listing and filters | | @x12i/ai-tools/sync | ModelNameResolver, OpenRouter fetch helper | | @x12i/ai-tools/profiles | resolveProfileForAsk, ai-profiles v3 re-exports | | @x12i/ai-tools/invoke | resolveInvokeModel, OpenRouter invoke routing helpers | | @x12i/ai-tools/toolbox | Tracker, router, guard |

Dependency: @x12i/ai-profiles ^3.3.0.

Tests

npm test
npm run build
npm run test:live    # optional OpenRouter API live test

License

MIT