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

elysia-ai-toolkit

v0.1.1

Published

Provider-agnostic AI toolkit for Elysia, thin over the Vercel AI SDK.

Downloads

320

Readme

elysia-ai-toolkit

Provider-agnostic AI toolkit for Elysia, built as a thin layer over the Vercel AI SDK. Configure providers once, define reusable agents, call tools, get structured output, and stream — all idiomatic to Elysia + Bun + TypeScript.

Install

bun add elysia-ai-toolkit

Peer deps (installed by you):

bun add ai elysia
bun add @ai-sdk/openai   # or @ai-sdk/anthropic, any AI SDK provider

zod is optional — only needed if you write tool/agent schemas in Zod instead of TypeBox.

Quick start

import { createAI } from "elysia-ai-toolkit";
import { createOpenAI } from "@ai-sdk/openai";

const openai = createOpenAI({ apiKey: process.env.OPENAI_API_KEY });

const ai = createAI({
  providers: { openai },
  models: {
    default: "openai:gpt-4o-mini",   // required
    smart: "openai:gpt-4o",          // alias → use "smart" anywhere a model is expected
  },
  defaults: { maxOutputTokens: 1024, temperature: 0.7 },
});

const { text } = await ai.generate({ prompt: "Explain Elysia in one line." });

Model references

A model is either "<provider>:<modelId>" or an alias key from config.models. Aliases can point at other aliases (cycles are detected at createAI time). Omit the model on a call and it falls back to the default alias.

Elysia plugin

ai.plugin() decorates the request context with the registry as ctx.ai:

import { Elysia, t } from "elysia";

const app = new Elysia()
  .use(ai.plugin())                          // ctx.ai available in every route
  .post("/ask", ({ ai, body }) => ai.generate({ prompt: body }).then(r => r.text), {
    body: t.String(),
  })
  .listen(3000);

The plugin is named (elysia-ai-toolkit), so repeated .use() dedupes. If ai collides with an existing decoration, rename the key: ai.plugin({ as: "llm" })ctx.llm.

Agents

Define instructions + model + tools + schema once, reuse everywhere. Two forms.

Factory form

const assistant = ai.agent({
  name: "Assistant",
  model: "smart",                    // optional, falls back to default
  instructions: "You are concise and precise.",
  tools: [/* … */],
  temperature: 0.3,
});

const { text } = await assistant.generate("What is a monad?");

Class form

import { Agent, tool } from "elysia-ai-toolkit";
import { t } from "elysia";

class Support extends Agent {
  name = "Support";
  instructions = "Help users with billing questions.";

  tools() {
    return [lookupInvoice];
  }
  // override schema(t) to force structured output
}

const support = ai.use(Support);
await support.generate({ prompt: "Where's my receipt?" });

Call input accepts three shapes: a bare string, { prompt: string }, or { messages: [...] } (full multi-turn history in the Vercel ModelMessage shape).

Tools

Tools are TypeBox-schema functions the model can call in a multi-step loop (default 8 steps when tools are present). handle receives typed args plus a ToolContext.

import { tool } from "elysia-ai-toolkit";
import { t } from "elysia";

const getWeather = tool({
  name: "getWeather",
  description: "Get the current weather for a city.",
  schema: t.Object({ city: t.String() }),
  handle: async ({ city }, ctx) => {
    // ctx.userId / ctx.request present when called inside an Elysia route
    // ctx.signal from the tool loop; ctx.agent.name = calling agent
    const res = await fetch(`https://api.example.com/w?q=${city}`);
    return res.json();
  },
});

Error policy: by default a thrown handle is caught and returned to the model as { error: "…" } so the loop can recover. Set failFast: true to abort the whole call instead (surfaces as ToolExecutionError).

Thread request context into tools from a route:

.post("/chat", ({ ai, body, request }) =>
  assistant.generate(body, { context: { userId: 42, request } }),
)

Structured output

Give an agent a schema and it returns validated JSON on result.object. Type the call to get the shape back:

const extractor = ai.agent({
  name: "Extractor",
  schema: t.Object({ name: t.String(), age: t.Number() }),
});

type Person = { name: string; age: number };
const { object } = await extractor.generate<Person>("John is 42.");
// object → { name: "John", age: 42 }

Invalid model output throws SchemaValidationError (carries raw output + issues). Both TypeBox and Zod schemas are accepted. Standalone helpers are exported too: validateOutput, validate, toJSONSchema, isTypeBox, isZod.

Streaming

ai.stream(...) and agent.stream(...) return a StreamHandle. Consume it exactly one way — a stream is single-consumer.

// 1. Async-iterate the text deltas (great with Elysia generator routes)
.post("/chat", async function* ({ body }) {
  for await (const delta of assistant.stream(body)) yield delta;
}, { body: t.String() })

// 2. Hand back a Response directly
const handle = assistant.stream("Tell me a story");
return handle.toResponse();      // text/plain deltas
return handle.sse();             // text/event-stream (data: <delta>)
return handle.toDataStream();    // Vercel data-stream protocol — for useChat / tool events

Get the aggregate result after the stream drains (combinable with any byte consumer):

const handle = assistant.stream("…");
handle.then(r => console.log(r.usage, r.finishReason));
// or: const result = await handle.result;

Result shape

generate() (and handle.result) resolve to a GenerateResult:

type GenerateResult<T = string> = {
  text: string;
  object?: T;                    // structured-output calls only
  usage: { inputTokens: number; outputTokens: number; totalTokens: number };
  steps: StepInfo[];             // per tool-loop round-trip
  toolCalls: ToolCallInfo[];     // { toolName, args, result?, error? }
  finishReason: string;
  raw: unknown;                  // underlying AI SDK result — escape hatch
};

Errors

All toolkit errors extend AIError. Notable subclasses:

| Error | When | |---|---| | ProviderNotConfiguredError | unknown provider in a model ref | | ModelDefaultNotConfiguredError | missing models.default or bad ref/alias | | SchemaValidationError | structured output failed validation | | ToolExecutionError | a failFast tool handle threw | | ProviderError | unclassified SDK/network error (preserves .cause) |

Rate-limit / overload / credit classes exist for future auto-failover.

Requirements

  • Bun
  • Peer deps: ai, elysia (+ your provider package, zod optional)

Scripts

bun test
bun run build