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

@plurnk/plurnk-providers

v0.37.1

Published

Framework + contract for plurnk LLM transports: built-in standard OpenAI-compatible providers plus scope-agnostic discovery of first-party and third-party provider packages.

Readme

plurnk-providers

Framework + contract for @plurnk/plurnk-providers-* sibling packages (LLM transports + tokenizer + cost accounting). Consumed by plurnk-service.

Documentation

Write a provider

Ship a provider by publishing a package — under any scope (@acme/whatever; discovery keys on plurnk.kind, not the @plurnk scope) — that declares its name and default-exports a fromEnv factory. (A plain OpenAI-compatible endpoint with no probe or wire quirk needs no package at all for first-party use — it's a frozen STANDARD_PROVIDERS entry; the package path is for bespoke providers and any third party.)

1. Declare the name in package.json

{
  "plurnk": { "kind": "provider", "name": "acme" }
}

One package is one provider identity — the <name> segment of PLURNK_MODEL_<alias>=<name>/<model>. (Unlike execs' runtimes[] array; a provider is singular.)

2. Default-export a fromEnv factory

The framework calls YourClass.fromEnv(env, model, options?) (sync or async) and expects a Provider. options.baseUrl is the per-alias endpoint override (PLURNK_BASEURL_<alias>) — honor it if you're a self-hosted provider so two aliases can reach two boxes; ignore it otherwise. Two ways in:

  • OpenAI-compatible backends (the common case): fromEnv reads its env (base URL, key), probes whatever it needs (catalog, context window, pricing), and returns new OpenAICompatProvider(config). You write a fromEnv and a config object — the transport spine (SSE, usage normalization, finishReason, grammar transport, slot affinity) is inherited. See OpenAICompatConfig / SPEC §11.
  • Non-OpenAI backends: implements Provider directly — generate, contextSize, model, countTokens(text), costFor(usage).

fromEnv MUST fail fast with a named error when required env is missing — name the var the operator must set. (Why a factory, not a base-class constructor like execs/mimes: a provider often async-probes at construction — SPEC §3.)

3. What generate receives — and returns

generate({ messages, runId, signal?, grammar?, maxTokens?, attributions?, client? }) → Promise<ProviderResponse>. Return raw wire output: content unparsed (the consumer parses the plurnk DSL — never parse it yourself), reasoning is the wire-reported CoT only. Honor signal. The provider never mutates messages or injects turns. grammar (GBNF) is attached only by backends that support it; all others ignore it (SPEC §13). When a grammar is transported, the provider verifies the backend actually enforced it — non-conforming output rejects with a grammar_unenforced ProviderError (a conformance check via @plurnk/gbnf, never a plurnk-DSL parse). In GBNF-filter mode (PLURNK_GBNF_DEBUG, grammar withheld) the same non-conformance is non-fatal: generate returns the model's bytes and attaches a grammar_unenforced event to ProviderResponse.telemetry with the divergence position, so the consumer can drive self-correction instead of losing the turn (#24). attributions/client are per-turn first-party metadata, forwarded as Plurnk-* headers only by a provider configured with firstPartyMetadata (the plurnk endpoint); every other provider drops them, so they can never reach a third-party backend (SPEC §11). The response carries a meta? bag — the backend's extra top-level fields passed through, plus validated known keys (e.g. meta.balancePico, pico-USD, normalized only from the plurnk endpoint) — for the service's per-turn metadata (#23).

Discovery & trust

discover(options?) scans every installed package under <cwd>/node_modules — scope-agnostic — for plurnk.kind === "provider", returning { registry, skipped, attributions } (registry/skipped: name → package specifier; attributions: name → the package's raw plurnk.attribution for author credit, #21).

  • Name collisions are fail-hard. Two packages claiming the same provider name throw at discovery, naming both.
  • The standard table wins. A scanned package whose name duplicates a built-in standard provider (openai, groq, …) is shadowed — tier 1 resolves first.
  • Trust gate. discover() honors PLURNK_PLUGINS_TRUSTED_ONLY (host posture, plurnk-service#229): unset/""/0 → every package registers (default, no regression); any value → @plurnk/* always trusted plus a comma-separated allowlist (1 = first-party only). An untrusted package is discovered but not registered (returned in Discovery.skipped), so requesting its name yields a precise untrusted error — never a crash.

First-party daughters install flat via @plurnk/plurnk-providers-all so the scan finds them; a third party publishes under their own scope and installs alongside.

Exports

  • Provider, ChatMessage, ProviderResponse, ProviderAssistant, ProviderUsage, FinishReason, ProviderFactory, ProviderOptions, ProviderAlias (+ Discovery, DiscoverOptions) — types.
  • parseAliasesFromEnv, resolveActiveAlias, instantiateProvider, loadActiveProvider, discover, resetDiscoveryCache — alias-cascade resolution + two-tier provider instantiation (resetDiscoveryCache clears the memoized tier-2 scan; for tests). Tier 1 is the standard table; tier 2 is a scope-agnostic node_modules scan for plurnk.kind:"provider" packages — first-party daughters (flat via @plurnk/plurnk-providers-all) and third-party providers under any scope, gated by the host PLURNK_PLUGINS_TRUSTED_ONLY allowlist. The framework is contract-only (SPEC §5).
  • OpenAICompatProvider (+ OpenAICompatConfig, ReasoningStyle, GrammarStyle, effortFromBudget) — shared OpenAI-compatible transport spine; siblings extend it (SPEC §11). Transports a GBNF grammar via grammarStylellamacpp (top-level grammar field) or response_format (Fireworks); none drops it — and verifies the backend enforced it against @plurnk/gbnf, rejecting non-conforming output as grammar_unenforced (SPEC §13).
  • chatCompletionStream, chatCompletion, OpenAiHttpError, StreamResponse — the shared SSE client (chatCompletion is the non-streaming variant).
  • parseRequiredInt, parseOptionalInt, requireEnv, reasoningBudgetFromEnv — env helpers (SPEC §4; all required-with-named-errors, no in-code defaults).
  • normalizeUsage, computeCost (+ RawUsage, TokenRates) — usage normalization to the §2 invariant and the single cost formula (SPEC §11).
  • ProviderError, classifyProviderError, toProviderError, providerSource (+ TelemetryEvent, ProviderTelemetryKind) — the TelemetryEvent envelope for transport failures (SPEC §12).
  • tokenizerFor, tokenizerByPublisher, parseTokenizerFamily (+ TokenizerFamily, CountTokens) — synchronous tokenizer strategies.
  • STANDARD_PROVIDERS, isStandardProvider, standardProviderFromEnv — pure-config OpenAI-compatible providers (no sibling package needed).
  • Mock (+ mockDefaultUsage) — reference implementation + test fixture (dual-purpose).

Tests

test:lint, test:unit.