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

@llm-workbench/ai-sdk

v0.4.0

Published

Vercel AI SDK wrappers that emit correlated model-I/O, tool-call, and cost trace events into an LLM Workbench run.

Readme

@llm-workbench/ai-sdk

MIT-licensed — Vercel AI SDK wrappers supporting AI SDK v5 and v7 for LLM Workbench. Call your model through these instead of the bare AI SDK functions, and each call automatically records a correlated model_io trace event (provider, model, token usage, duration) — plus tool_call events and AI-Gateway cost — into the active run.

npm install @llm-workbench/ai-sdk @llm-workbench/runtime ai zod

API surface

| Export | Wraps (from ai) | | --- | --- | | tracedGenerateText / tracedStreamText | generateText / streamText | | tracedGenerateObject / tracedStreamObject (deprecated) | generateObject / streamObject | | traceTools | a tool set, so each invocation logs a tool_call event | | costFromGatewayMetadata | derives cost from Vercel AI Gateway response metadata |

Each traced* call takes a WorkbenchSession first, then an options object: for example, tracedGenerateText(session, opts). The options combine the corresponding AI SDK options with WorkbenchTraceContext (stepId?, correlationId?, detail?) and an optional writeArtifact hook. See the typed options (TracedGenerateTextOptions, etc.) and the reference wiring in apps/web.

Supported peers are ai: ^5.0.0 || ^7.0.0 and zod: ^3.23.8 || ^4.0.0.

Migrating from tracedGenerateObject / tracedStreamObject

AI SDK deprecated generateObject and streamObject in version 6 in favor of generateText / streamText with an output setting. The plain output API requires AI SDK 6 or later, including 7; use AI SDK 7 with this package's current peer range, which excludes 6. AI SDK 5 uses experimental_output instead. Both versions preserve schema-inferred output through the text wrappers; the examples below identify the SDK they use.

The deprecated wrappers keep working unchanged and will be removed only after AI SDK removes the underlying functions. No removal version has been announced in the linked migration guide.

Generating an object

Before:

import { tracedGenerateObject } from "@llm-workbench/ai-sdk";
import type { WorkbenchSession } from "@llm-workbench/runtime";
import type { LanguageModel } from "ai";
import { z } from "zod";

declare const session: WorkbenchSession;
declare const model: LanguageModel; // Your configured provider model.
const schema = z.object({ name: z.string(), count: z.number() });

const result = await tracedGenerateObject(session, {
  model,
  prompt: "Generate a profile",
  schema,
  writeArtifact: { artifactKey: "profile", typeId: "profile" },
});
const profile = schema.parse(result.object);

After (AI SDK 7):

import { tracedGenerateText } from "@llm-workbench/ai-sdk";
import type { WorkbenchSession } from "@llm-workbench/runtime";
import { Output, type LanguageModel } from "ai";
import { z } from "zod";

declare const session: WorkbenchSession;
declare const model: LanguageModel;
const schema = z.object({ name: z.string(), count: z.number() });

const result = await tracedGenerateText(session, {
  model,
  prompt: "Generate a profile",
  output: Output.object({ schema }),
  writeArtifact: {
    artifactKey: "profile",
    typeId: "profile",
    toData: (r) => r.output,
  },
});
const profile: z.infer<typeof schema> = result.output; // { name: string; count: number }

Supply toData when persisting an object. The text wrappers' default artifact projectors persist result.text (the JSON string), even when output: Output.object({ schema }) returns a parsed object. toData overrides that projector. Adding a registry to writeArtifact validates the projected data against typeId; an invalid artifact throws WorkbenchError with code INVALID_INPUT for tracedGenerateText.

Previously, tracedGenerateText erased the output generic, so SDK 7's result.output was any and required schema.parse(result.output) to recover its type. It now infers z.infer<typeof schema> directly. SDK 5 likewise infers result.experimental_output. The SDK still parses and validates the response at runtime. Calls without an output specification, and options variables already annotated as TracedGenerateTextOptions, retain their existing result types. writeArtifact.toData keeps its existing loose parameter type; it does not infer the specific schema from the enclosing options.

After (AI SDK 5.0.273):

import { tracedGenerateText } from "@llm-workbench/ai-sdk";
import type { WorkbenchSession } from "@llm-workbench/runtime";
import { Output, type LanguageModel } from "ai";
import { z } from "zod";

declare const session: WorkbenchSession;
declare const model: LanguageModel;
const schema = z.object({ name: z.string(), count: z.number() });

const result = await tracedGenerateText(session, {
  model,
  prompt: "Generate a profile",
  experimental_output: Output.object({ schema }),
  writeArtifact: {
    artifactKey: "profile",
    typeId: "profile",
    toData: (r) => schema.parse(r.experimental_output),
  },
});
const profile: z.infer<typeof schema> = result.experimental_output;

Streaming an object

Before:

import { tracedStreamObject } from "@llm-workbench/ai-sdk";
import type { WorkbenchSession } from "@llm-workbench/runtime";
import type { LanguageModel } from "ai";
import { z } from "zod";

declare const session: WorkbenchSession;
declare const model: LanguageModel;
const schema = z.object({ name: z.string(), count: z.number() });

const stream = tracedStreamObject(session, {
  model,
  prompt: "Generate a profile",
  schema,
  writeArtifact: { artifactKey: "profile", typeId: "profile" },
});
for await (const partial of stream.partialObjectStream) {
  void partial; // Update your UI with each partial object.
}
const profile = schema.parse(await stream.object);

After (tested with AI SDK 7):

import { tracedStreamText } from "@llm-workbench/ai-sdk";
import type { WorkbenchSession } from "@llm-workbench/runtime";
import { Output, type LanguageModel } from "ai";
import { z } from "zod";

declare const session: WorkbenchSession;
declare const model: LanguageModel;
const schema = z.object({ name: z.string(), count: z.number() });

const stream = tracedStreamText(session, {
  model,
  prompt: "Generate a profile",
  output: Output.object({ schema }),
  writeArtifact: {
    artifactKey: "profile",
    typeId: "profile",
    toData: ({ result }) => schema.parse((result as { output: unknown }).output),
  },
});
for await (const partial of stream.partialOutputStream) {
  void partial; // Update your UI; partial values may have missing fields.
}
const profile: z.infer<typeof schema> = await stream.output;

Consume partialOutputStream to completion so the stream finishes and emits the final response trace with usage. Streaming toData receives { text: string, result: unknown }: text is the final JSON string and result is the SDK finish event, not the returned stream handle. In AI SDK 7 that event includes the parsed output; narrow its unknown type and validate with schema.parse, as shown. Without toData, the artifact is again the JSON string. If registry validation or artifact persistence fails during streaming, the wrapper emits an artifact_write_failed trace; it does not reject the final structured output.

Both versions infer the partial object fields in their output streams. SDK 7 also infers the awaited stream.output, and the wrappers preserve the SDK's Output.text(), Output.array(), Output.choice(), and Output.json() types. SDK 5 supports Output.text() and Output.object(); it has no complete structured-output promise on the stream handle and no array/choice/json output factories.

After (AI SDK 5.0.273):

import { tracedStreamText } from "@llm-workbench/ai-sdk";
import type { WorkbenchSession } from "@llm-workbench/runtime";
import { Output, type LanguageModel } from "ai";
import { z } from "zod";

declare const session: WorkbenchSession;
declare const model: LanguageModel;
const schema = z.object({ name: z.string(), count: z.number() });

const stream = tracedStreamText(session, {
  model,
  prompt: "Generate a profile",
  experimental_output: Output.object({ schema }),
  writeArtifact: {
    artifactKey: "profile",
    typeId: "profile",
    toData: ({ text }) => schema.parse(JSON.parse(text)),
  },
});
for await (const partial of stream.experimental_partialOutputStream) {
  const name: string | undefined = partial.name;
  void name; // Update your UI with each partial object.
}
const profile = schema.parse(JSON.parse(await stream.text));

The SDK 5 example parses the completed text to obtain the final object and artifact data. Its default artifact projector still writes that text string.

Docs

  • Overview and architecture: repository root README.md.
  • Getting started: https://www.llmworkbench.io/docs/getting-started
  • Architecture deep-dive: https://www.llmworkbench.io/docs/architecture
  • Generated API reference: https://www.llmworkbench.io/docs/api