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

@mozilla-ai/otari

v0.4.0

Published

TypeScript SDK for the otari gateway

Downloads

108

Readme

Otari TypeScript Client SDK

Node.js 24+ npm

TypeScript client for otari, the open-source core that powers otari.ai. Talk to any LLM provider through otari using a single, typed interface.

Hosted Platform (Beta)

New to otari? The otari repo explains what it is and why you’d use it.

Quickstart

Install:

npm install @mozilla-ai/otari

Generate an API token at otari.ai/organization-settings/api-tokens, then add a provider key (e.g. OpenAI) at otari.ai/organization-settings/provider-keys so the gateway can route requests to that provider. Then use the client:

import { OtariClient } from "@mozilla-ai/otari";

const client = new OtariClient({
  platformToken: "tk_your_api_token",
});

const response = await client.completion({
  model: "openai:gpt-4o-mini",
  messages: [{ role: "user", content: "Hello!" }],
});

console.log(response.choices[0].message.content);

That's it, the client defaults to the hosted gateway at https://api.otari.ai. Change the model string to switch providers.

Installation

Requirements

Node.js 24+.

Install

npm install @mozilla-ai/otari

Setting up credentials

The client reads credentials from constructor options or environment variables.

  • Platform (hosted otari.ai): set OTARI_AI_TOKEN to your API token. The base URL defaults to https://api.otari.ai.
  • Self-hosted gateway: set GATEWAY_API_BASE to your gateway URL and GATEWAY_API_KEY to your gateway API key.

Prefer to keep secrets out of code? Create a .env (copy .env.example) and run with node --env-file=.env your-script.js. Node 20.6+ loads it natively, with no extra dependency:

OTARI_AI_TOKEN=tk_your_api_token

Then new OtariClient() picks up the token from the environment.

Authentication

The client supports two modes.

Platform mode (hosted): pass platformToken (or set OTARI_AI_TOKEN). The token is sent as Authorization: Bearer <token>, and the base URL defaults to the hosted gateway at https://api.otari.ai:

const client = new OtariClient({
  platformToken: "tk_your_api_token",
});

Self-hosted mode: run the gateway yourself (see the otari repo), then point the SDK at it with apiBase and apiKey. The API key is sent via the custom Otari-Key: Bearer <key> header:

const client = new OtariClient({
  apiBase: "http://localhost:8000", // or wherever you host the gateway
  apiKey: "your-gateway-api-key",
});

These map to the GATEWAY_API_BASE and GATEWAY_API_KEY environment variables. Make sure your gateway has provider keys configured (e.g. OpenAI) so it can route requests upstream; see the otari repo for setup.

Usage

Chat completions

const response = await client.completion({
  model: "openai:gpt-4o-mini",
  messages: [{ role: "user", content: "Hello!" }],
});
console.log(response.choices[0].message.content);

Streaming

Pass stream: true to get an AsyncIterable of chunks, and iterate with for await:

const stream = await client.completion({
  model: "openai:gpt-4o-mini",
  messages: [{ role: "user", content: "Tell me a story." }],
  stream: true,
});

for await (const chunk of stream) {
  const content = chunk.choices[0]?.delta?.content;
  if (content) process.stdout.write(content);
}

Responses API

const response = await client.response({
  model: "openai:gpt-4o-mini",
  input: "Summarize this in one sentence.",
});
console.log(response.output_text);

Messages API

The Anthropic-shaped /messages endpoint requires max_tokens and returns content blocks:

const message = await client.message({
  model: "anthropic:claude-3-5-sonnet",
  messages: [{ role: "user", content: "Hello!" }],
  max_tokens: 1024,
});
console.log(message.content);

Embeddings

const result = await client.embedding({
  model: "openai:text-embedding-3-small",
  input: "Hello world",
});
console.log(result.data[0].embedding);

Listing models

const models = await client.listModels();
for (const model of models) {
  console.log(model.id);
}

Moderation

import { OtariClient, UnsupportedCapabilityError } from "@mozilla-ai/otari";

try {
  const result = await client.moderation({
    model: "openai:omni-moderation-latest",
    input: "I want to hurt someone",
  });
  if (result.results[0].flagged) {
    throw new Error("unsafe input");
  }
} catch (err) {
  if (err instanceof UnsupportedCapabilityError) {
    // The selected provider doesn't offer moderation (e.g. Anthropic).
    console.error(`${err.provider} does not support ${err.capability}`);
  } else {
    throw err;
  }
}

Pass includeRaw: true to preserve the upstream provider's raw body under provider_raw:

const result = await client.moderation({
  model: "openai:omni-moderation-latest",
  input: "...",
  includeRaw: true,
});
console.log(result.results[0].provider_raw);

Reranking

Rerank documents by relevance to a query. Results carry the original index and a relevanceScore:

const result = await client.rerank({
  model: "cohere:rerank-v3.5",
  query: "What is the capital of France?",
  documents: ["Paris is the capital of France.", "Berlin is in Germany."],
  top_n: 2,
});
for (const item of result.results ?? []) {
  console.log(item.index, item.relevanceScore);
}

Batch operations

Submit many requests as a single batch, poll for status, then fetch results:

const batch = await client.createBatch({
  model: "openai:gpt-4o-mini",
  requests: [
    {
      custom_id: "r1",
      body: { messages: [{ role: "user", content: "Hello!" }] },
    },
  ],
});

// The provider is returned on the batch and required for follow-up calls.
const status = await client.retrieveBatch(batch.id, batch.provider);

// List batches for a provider (optional pagination).
const batches = await client.listBatches(batch.provider, { limit: 10 });

// Once complete, fetch the per-request results.
const { results } = await client.retrieveBatchResults(batch.id, batch.provider);
for (const entry of results) {
  console.log(entry.custom_id, entry.result ?? entry.error);
}

// Or cancel an in-flight batch.
await client.cancelBatch(batch.id, batch.provider);

retrieveBatchResults throws BatchNotCompleteError (HTTP 409) if the batch is not yet complete.

Error handling

In platform mode, HTTP errors are mapped to typed exceptions:

import { OtariClient, AuthenticationError, RateLimitError } from "@mozilla-ai/otari";

try {
  await client.completion({
    model: "openai:gpt-4o-mini",
    messages: [{ role: "user", content: "Hello!" }],
  });
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.error("Invalid credentials:", error.message);
  } else if (error instanceof RateLimitError) {
    console.error("Rate limited, retry after:", error.retryAfter);
  }
}

| HTTP Status | Error Class | Description | |------------|-------------|-------------| | 400 (capability) | UnsupportedCapabilityError | Selected provider does not support the requested capability (e.g. moderation) | | 401, 403 | AuthenticationError | Invalid or missing credentials | | 402 | InsufficientFundsError | Budget or credits exhausted | | 404 | ModelNotFoundError | Model not found, or no provider key configured for the requested provider, add one at otari.ai/organization-settings/provider-keys. The exception's message contains the gateway's detail. | | 409 | BatchNotCompleteError | Batch results requested before the batch completed | | 429 | RateLimitError | Rate limit exceeded (includes retryAfter) | | 502 | UpstreamProviderError | Upstream provider unreachable | | 504 | GatewayTimeoutError | Gateway timed out waiting for provider |

UnsupportedCapabilityError surfaces in both modes; the rest are platform-mode only.

Development

Requires Node.js 24+.

npm install        # install deps
npm run test:unit  # unit tests
npm test           # all tests
npm run typecheck  # type-check
npm run build      # build to dist/

Documentation

Full documentation lives at mozilla-ai.github.io/otari.

otari also ships client SDKs in other languages, all targeting the same gateway:

Contributing

Contributions welcome, please open an issue or PR. See the contributing guide on the main otari repo.

License

Apache License 2.0, see LICENSE.