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

v5.1.0

Published

AI model catalogs via @x12i/ai-profiles, cost calculation, and LLM utilities

Readme

@x12i/ai-tools

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

Catalog source of truth: @x12i/ai-profiles ^4.2.1 — profile registry + unified models catalog (direct vendor + OpenRouter slices on each entry). Remote asset: models-catalog.v2.json.

Resolution order

  1. Known profile input@x12i/ai-profiles (resolveInput / isKnownProfileInput)
  2. Concrete model → ai-profiles identity + unified catalog
  3. Fuzzy matchModelNameResolver over the ai-profiles-loaded catalog map
  4. Pattern last-resort (vendor inference) only when nothing above hits

v5.0 breaking changes

  • Removed ai-tools-owned dual catalogs (src/data/*.json, legacy open-assets URLs).
  • Profile-first resolve (see above). Do not pass profile/choice through vendor-slug parsers.

v3.0 breaking changes

Aligned with @x12i/ai-profiles v3+ (current dependency ^4.2.1).

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 ^4.2.1.

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 (profile → 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,
  isKnownProfileInput,
  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"

isKnownProfileInput("cheap/default"); // true
isKnownProfileInput("google/gemma-3-4b-it"); // false
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 load via @x12i/ai-profiles (loadModelsCatalog) on first use and are cached in memory for 24 hours. Override TTL with AI_TOOLS_CACHE_TTL_MS. Use bundledOnly: true / --bundled-only to skip remote and use the ai-profiles bundled catalog.

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

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. deepseek/deepseek-v4-flash | | standard, cheapest | not found (use balanced/default, cheap/default) | | deepseekv32 | ai-profiles alias → deepseek-v3.2 (use deepseek/deepseek-v3.2 for explicit SKU) | | gemma-3-4b-it | 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
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 re-exports | | @x12i/ai-tools/invoke | resolveInvokeModel, OpenRouter invoke routing helpers | | @x12i/ai-tools/toolbox | Tracker, router, guard |

Dependencies: @x12i/ai-profiles ^4.2.1, @x12i/env ^4.0.3.

Runtime targets

@x12i/ai-tools targets Node ≥20 (CLI, catalog HTTP fetch, optional Mongo-shaped usage extraction). Subpaths such as ./cost or pure resolver helpers may run in other runtimes when you avoid Node-only APIs — but profile resolution pulls in @x12i/ai-profiles, which depends on @x12i/logxer@^5.1.0 for Node-side registry tooling.

This package does not import @x12i/logxer directly.

@x12i/[email protected] — ESM/CJS and import boundaries

@x12i/[email protected] was CJS-only ("type": "commonjs", single "default" export). Vite and other ESM hosts that did import { createLogxer } from '@x12i/logxer' hit CJS/ESM interop failure even though createLogxer existed on the CJS export object.

@x12i/[email protected] fixes this with dual ESM + CJS builds (real named exports in both), a browser-safe @x12i/logxer/package-levels subpath, and a node-only stub on the main entry so accidental browser imports fail with a clear message.

When your app also uses logxer alongside ai-tools, follow subpath rules so Vite client bundles never pull the Node logger:

| Need | Import from | Runs in | |------|-------------|---------| | Full logger factory, Mongo persistence, sessions, fetch | @x12i/logxer | Node only (BFF, execute harness, SSR with Node externals) | | Env key naming / parse package level strings | @x12i/logxer/package-levels | Browser + Node | | Diagnostic evidence helpers, catalog types | @x12i/logxer/diagnostics | Browser-safe for pure helpers; check each symbol | | Agent instruction docs | @x12i/logxer/agent-instructions | Node / tooling |

Never import @x12i/logxer (main) from Vite client bundles or any file that lands in the browser graph.

// Browser / Vite client — env key naming only
import { packageLogsLevelEnvKey } from "@x12i/logxer/package-levels";

// Node / SSR / BFF — full logger (requires @x12i/logxer@^5.1.0 for ESM named exports)
import { createLogxer } from "@x12i/logxer";

Upgrade: npm install @x12i/logxer@^5.1.0 (pulled transitively via @x12i/ai-profiles@^3.4.1 when you install ai-tools).

Tests

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

License

MIT