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

@tsaitechnology/tsaitech-ai-chat

v1.3.1

Published

Framework-neutral browser knowledge retrieval engine for the TSAI Technology portfolio chat

Readme

@tsaitechnology/tsaitech-ai-chat

Framework-neutral browser retrieval engine and curated knowledge base for the TSAI Technology portfolio chat. The package embeds a user query locally with a pinned Transformers.js model, resolves deterministic multi-document answer recipes first, and keeps single-document semantic and lexical retrieval as its fallback.

The runtime does not generate answers, execute user instructions, or require an inference backend.

Frontend requirements

The consuming application must provide:

  • a modern browser with ES modules, Web Workers, WebAssembly, Fetch API, Web Crypto, AbortController, TextEncoder, Intl.Segmenter, and typed arrays;
  • a bundler capable of building module workers, such as Vite;
  • same-origin static hosting for the generated database artifacts;
  • UI states for initialization progress, answers, clarification, out-of-scope queries, and runtime failures;
  • cancellation of an active request when the user submits a replacement query or leaves the chat;
  • plain-text rendering for answer.text and answer.details;
  • normal safe anchors for the already validated evidence URLs.

React, Vue, Svelte, and other UI frameworks are not runtime dependencies.

Installation

npm install @tsaitechnology/tsaitech-ai-chat

The npm package contains the runtime JavaScript, TypeScript declarations, module worker, and curated database artifacts.

Static knowledge artifacts

Copy the installed package's database into the frontend's public/static directory without renaming generated files:

npx tsaitech-ai-chat-copy-db public/db

Add the command to the frontend build so an installed package update also updates its published knowledge artifacts. For example:

{
  "scripts": {
    "knowledge:sync": "tsaitech-ai-chat-copy-db public/db",
    "prebuild": "npm run knowledge:sync",
    "build": "vite build"
  }
}

The resulting frontend structure is:

public/db/
  answer-recipes.yaml
  manifest.json
  vectors.<content-hash>.f32
  lexical-index.<content-hash>.json
  **/*.md

The manifest controls every path the runtime may request. A query must never be appended to a URL or converted into a document path.

The frontend serves these files from its own origin at /db/. No GitHub Pages deployment or request back to this repository is required. The HTTP repository intentionally rejects cross-origin redirects.

HTTP contract

Static hosting must return:

| Resource | Content-Type | Cache policy | |---|---|---| | manifest.json | application/json | no-cache | | *.json lexical index | application/json | immutable by versioned filename | | *.f32 vectors | application/octet-stream | immutable by versioned filename | | *.md documents | text/markdown or text/plain | may use ETag/content hash |

HTML fallback responses are rejected. The manifest, lexical index, vectors, embedding profile, and selected Markdown content must all belong to the same generated content version.

Vite integration

Use the exported worker entry so Vite bundles Transformers.js and its worker dependencies:

/// <reference types="vite/client" />

import EmbedderWorker from "@tsaitechnology/tsaitech-ai-chat/worker?worker";
import {
  createHttpKnowledgeRepository,
  createKnowledgeEngine,
  createWasmEmbedder,
} from "@tsaitechnology/tsaitech-ai-chat";

const engine = createKnowledgeEngine({
  repository: createHttpKnowledgeRepository({
    baseUrl: new URL("/db/", window.location.origin),
    fetch: window.fetch.bind(window),
  }),
  embedder: createWasmEmbedder({
    workerFactory: () => new EmbedderWorker(),
  }),
});

await engine.initialize({
  warmup: true,
  onProgress(event) {
    // Map event.phase to application loading state.
    console.log(event.phase, event.fraction);
  },
});

Create one engine per application session. Do not initialize a new model for every component render or message.

Chat request lifecycle

Only submit complete messages. Do not run embeddings on each keystroke.

let activeRequest: AbortController | undefined;
let previousAnswerId: string | undefined;

async function sendMessage(query: string) {
  activeRequest?.abort();
  activeRequest = new AbortController();

  const result = await engine.answer(
    {
      query,
      ...(previousAnswerId ? { previousAnswerId } : {}),
    },
    { signal: activeRequest.signal, timeoutMs: 30_000 },
  );

  switch (result.kind) {
    case "answer":
      previousAnswerId = result.answer.id;
      renderPlainTextAnswer(result.answer);
      break;
    case "clarification":
      renderClarification(result.message, result.options);
      break;
    case "out_of_scope":
      renderSystemMessage(result.message);
      break;
    case "unavailable":
      renderUnavailable(result.message, result.retryable);
      break;
  }
}

previousAnswerId is the only conversational context required by the engine. Do not pass a raw transcript to the embedder.

Call await engine.dispose() when the application permanently tears down the chat. It releases the worker and model resources.

Public contract

The main API is KnowledgeEngine:

interface KnowledgeEngine {
  initialize(options?: InitializeOptions): Promise<EngineInfo>;
  getTree(options?: GetTreeOptions): Promise<KnowledgeTree>;
  getDocument(id: string, options?: RequestOptions): Promise<KnowledgeDocument>;
  search(request: SearchRequest, options?: RequestOptions): Promise<readonly SearchHit[]>;
  answer(request: AnswerRequest, options?: RequestOptions): Promise<AnswerResult>;
  getState(): EngineState;
  dispose(): Promise<void>;
}

Use answer() for production chat. It owns confidence, ambiguity, scope, expiry, and artifact-integrity decisions. search() is diagnostic and can intentionally return low-confidence results; frontend code must not select its first hit as an answer.

The complete contract and failure reasons are documented in docs/API-CONTRACT.md. Runtime-specific deployment notes are in docs/RUNTIME-RETRIEVAL.md.

Rendering and security

  • Treat answer.text and answer.details as plain text, not HTML.
  • Preserve line breaks in answer.text (for example with white-space: pre-wrap); sentences and composed source sections are intentionally separated by blank lines.
  • Do not run the returned text through an unsafe Markdown-to-HTML renderer.
  • Evidence URLs are restricted to HTTP(S), but external links should still use the frontend's normal safe-link policy.
  • Never interpolate the query into answer HTML, evidence URLs, storage keys, or repository paths.
  • Do not bypass answer() thresholds using search() scores.
  • Limit the input UI to 500 Unicode characters to match the engine boundary.
  • Do not truncate answer.text; the runtime has no output character limit and detailed compositions are intentionally long.
  • The model executes in a dedicated worker and must not be given network, tools, or storage capabilities by frontend code.

The pinned model currently loads through Transformers.js. Production deployments may additionally self-host the model, tokenizer, and ONNX Runtime WASM assets. WASM threads are optional; enabling them requires appropriate COOP/COEP headers and crossOriginIsolated.

Local verification

npm ci
npm run verify
npm run build
npm pack --dry-run

npm run verify runs TypeScript checking, database validation, unit/contract tests, deterministic answer-quality evaluation, retrieval evaluation, and the pinned-model integration test.

Publishing to npm

The repository contains a manual GitHub Actions workflow: .github/workflows/publish-npm.yml.

Repository setup:

  1. Configure npm Trusted Publishing for GitHub organization tsaitechnology, repository tsaitech-ai-chat, and workflow filename publish-npm.yml.
  2. Allow the trusted publisher to run npm publish. Do not configure an environment name unless the workflow uses the same GitHub environment.
  3. Open Actions > Publish npm package > Run workflow.
  4. Enter an exact unused semver such as 1.0.0 and choose the latest or next npm tag.

The workflow uses npm Trusted Publishing with GitHub OIDC, so it does not require NPM_TOKEN. It installs with npm ci, sets the requested package version without committing it, runs the complete verification suite, builds dist, inspects the package payload, and publishes using a short-lived credential. Sigstore provenance is explicitly disabled because npm only supports provenance for public source repositories.

The package is published as @tsaitechnology/tsaitech-ai-chat. The trusted publisher configuration must belong to that npm organization and exactly match this GitHub repository and workflow filename.

License

The package is currently marked UNLICENSED. Choose and add a repository license before public third-party distribution if that is intended.