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

@opensmartroute/sdk

v1.9.0

Published

TypeScript client for the OpenSmartRoute platform and osr serve: routing, feedback, quotes and the OpenAI-compatible endpoint (fetch-based, zero dependencies).

Readme

@opensmartroute/sdk

TypeScript client for OpenSmartRoute: the hosted platform (https://opensmartroute.ai) and a self-hosted osr serve. Zero runtime dependencies - it uses the global fetch, so it runs on Node 20+, Deno, Bun, Cloudflare Workers, Vercel Edge and in browsers.

npm install @opensmartroute/sdk        # pnpm add / yarn add / bun add work the same

The package version follows the OpenSmartRoute release (1.9.0 today): every release publishes it to the npm registry, and the types under src/generated are generated from that release's OpenAPI document. ESM and CommonJS entry points with bundled .d.ts; import { OpenSmartRoute } from "@opensmartroute/sdk" or const { OpenSmartRoute } = require("@opensmartroute/sdk").

Quick start: route, then report

import { OpenSmartRoute } from "@opensmartroute/sdk";

const osr = new OpenSmartRoute({
  baseUrl: "https://opensmartroute.ai",
  apiKey: process.env.OSR_API_KEY!,
});

const decision = await osr.route({
  text: "Refund order #123, the customer is upset",
  objective: { cost: 0.4, quality: 0.6 },
  constraints: { data_boundary: "private" },
  top_k: 3,
});
console.log(decision.target.id, decision.confidence, decision.explanation);

// ...call the chosen target, then close the loop so the learners improve on your traffic
await osr.feedback({
  request_id: decision.request_id,
  target_id: decision.target.id,
  success: true,
  quality: 0.9,
  latency_ms: 850,
});

route({ ..., execute: true }) also runs the target on the platform and returns the answer in decision.result (text, provider, cost_usd, fallback_from). plan: true composes a persona -> skill -> model plan, plus the tools the model may call (decision.plan). estimate(req) quotes a request before sending it; targets() lists the catalogue; me(), quota() and info() describe the account, where it stands against its monthly request quota, and the deployment.

OpenAI-compatible chat, streaming and embeddings

The same client speaks the platform's /v1 surface. model: "auto" (the default) lets the router choose; a target id pins one; models: [...] gives the router a candidate list with fallbacks. Every answer carries the routing decision in opensmartroute (typed as OsrMetadata).

const reply = await osr.chat.completions.create({
  model: "auto",
  messages: [{ role: "user", content: "Write a haiku about routing." }],
  osr: { objective: { cost: 0.7 }, fallbacks: true },
});
console.log(reply.model, reply.choices[0].message.content);
console.log(reply.opensmartroute.target, reply.opensmartroute.confidence, reply.opensmartroute.cost_usd);

// streaming: an AsyncIterable of chat.completion.chunk objects, parsed from the SSE stream
const stream = await osr.chat.completions.create({
  messages: [{ role: "user", content: "Explain LinUCB in two sentences." }],
  stream: true,
});
console.log("answering:", stream.target); // known from the X-OSR-Target header before the first token
for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta.content ?? "");
  if (chunk.opensmartroute) console.log("\n", chunk.usage, chunk.opensmartroute); // the last chunk
}
// or: const completion = await stream.finalCompletion();

const vectors = await osr.embeddings.create({ input: ["first text", "second text"] });
console.log(vectors.data[0].embedding.length, vectors.opensmartroute.target);

const models = await osr.models.list(); // "auto", "osr/auto:cheap", ... and every executable target

Responses API, Messages API, speech, transcription, images

The OpenAI Responses API (what the OpenAI Agents SDK speaks) and the Anthropic Messages API (what the Anthropic SDKs and Claude Code speak) run over the same routed path; media endpoints route among the targets that can do the job:

const r = await osr.responses.create({ input: "Plan a 3-day trip to Oslo", instructions: "Be concise." });
console.log(responseText(r), r.opensmartroute?.target);
const s = await osr.responses.create({ input: "Explain LinUCB", stream: true });
for await (const ev of s) if (ev.type === "response.output_text.delta") process.stdout.write(ev.delta!);
const final = await s.finalResponse();

const m = await osr.messages.create({ max_tokens: 256, messages: [{ role: "user", content: "Explain LinUCB" }] });
console.log(messageText(m), m.stop_reason);
const ms = await osr.messages.create({ max_tokens: 256, messages: [{ role: "user", content: "hi" }], stream: true });
const done = await ms.finalMessage(); // text and tool_use blocks assembled from the events
const { input_tokens } = await osr.messages.countTokens({ messages: [{ role: "user", content: "hi" }] });

const speech = await osr.audio.speech.create({ input: "Hello there", response_format: "mp3" }); // bytes + headers
const text = await osr.audio.transcriptions.create({ file: audioBlob, filename: "memo.wav" });
const pic = await osr.images.generate({ prompt: "a lighthouse at dawn", size: "1024x1024" });

Workspace management (platform)

Every route of the platform's OpenAPI document is reachable, typed from the generated schema, through osr.rest - paths, path / query parameters, bodies and responses are checked by tsc:

const keys = await osr.rest.get("/api/v1/keys");
await osr.rest.post("/api/v1/keys", { body: { name: "ci", scopes: ["route", "feedback"], expires_in_days: 90 } });
await osr.rest.del("/api/v1/keys/{key_id}", { path: { key_id: keys[0].id } });

Convenience groups sit on top of it: osr.keys (list / create / rename / rotate / revoke), osr.tenants, osr.governance (policy), osr.providers (bring your own model providers), osr.notifications, osr.support, osr.handoffs, osr.billing, osr.marketplace and osr.insights (usage, activity, savings, audit trail, statistics, rankings, SLA).

Against osr serve

osr serve mounts the same routes at the root (no /api/v1 prefix) and takes a bearer token:

osr -t targets.yaml -r rules.yaml serve --port 8000 --token my-secret-token
const local = new OpenSmartRoute({
  baseUrl: "http://localhost:8000",
  apiKey: "my-secret-token",
  apiPrefix: "", // osr serve: /route, /feedback, /targets; /v1/... is the same
});
const who = await local.whoami(); // { edition: "self-hosted", authenticated: true, ... }
const d = await local.route({ text: "Summarise this ticket" });

Options and headers

| Option | Header / effect | |---|---| | apiKey | Authorization: Bearer <key> (platform API key, or an osr serve token) | | tenant / opts.tenant | X-OSR-Tenant - the tenant's constraints and budget apply | | app | X-OSR-App - attribution echoed in the decision metadata | | opts.requestId | X-Request-Id - your own id, echoed back and usable for feedback | | headers, opts.headers | extra headers for every / this request | | timeoutMs, opts.timeoutMs | abort when no headers arrive in time (streams keep flowing) | | opts.signal | your AbortSignal (also cancels a stream) | | fetch | a custom fetch (tests, proxies, instrumentation) |

requestWithResponse(method, path, body) returns the decoded body and the Response, for headers such as X-OSR-Target, X-OSR-Trace-Id, ETag or the quota headers.

Errors

Every non-2xx answer raises OpenSmartRouteError with status, detail (the server's message), requestId (X-Request-Id / X-OSR-Request-Id), retryAfter (seconds, from Retry-After), headers and a stable code:

| Class | Status | code | |---|---|---| | AuthenticationError | 401 | authentication | | PermissionDeniedError (upgradeRequired when X-Upgrade: true) | 403 | permission | | NotFoundError | 404 | not_found | | RateLimitError (budget from the X-Budget-* headers) | 429 | rate_limit | | OpenSmartRouteError | 400 / 413 / 422 | validation | | OpenSmartRouteError | 501 / 502-504 / other 5xx | not_implemented / upstream / server | | ConnectionError | - (no response) | network |

import { RateLimitError } from "@opensmartroute/sdk";

try {
  await osr.route({ text: "..." });
} catch (e) {
  if (e instanceof RateLimitError) await sleep((e.retryAfter ?? 1) * 1000);
  else throw e;
}

Types

RouteIn, FeedbackIn, EstimateIn, ChatCompletionIn, EmbeddingsIn and RoutingOptions are generated from the platform's OpenAPI document (src/generated/openapi.ts, also exported as OpenAPIPaths / OpenAPIComponents for openapi-fetch users). RouteDecision, OsrMetadata, ChatCompletion, ChatCompletionChunk, EmbeddingsResponse, ModelList, TargetInfo, Estimate and Me describe the answers.

Development

npm ci
npm run generate   # regenerate src/generated/openapi.ts from ../api/openapi.json (commit the result)
npm run check      # eslint + tsc + vitest
npm run build      # dist/ (ESM + CJS + .d.ts) with tsup

The live test runs only when OSR_SDK_BASE_URL and OSR_SDK_API_KEY are set (OSR_SDK_API_PREFIX forces /api/v1 or an empty prefix; without it the test probes GET /whoami to recognise osr serve). Against a local server (pip install "opensmartroute[server,yaml]"):

osr -t targets.yaml -r rules.yaml serve --port 8765 --token sdk-test-token
# in another shell
$env:OSR_SDK_BASE_URL = "http://127.0.0.1:8765"; $env:OSR_SDK_API_KEY = "sdk-test-token"
npm test --prefix platform/sdk-ts

Without a model provider configured the chat and embeddings calls answer with a well-formed error (the test accepts either outcome); routing, feedback, targets and models are asserted fully.

License

Apache-2.0 - see LICENSE.