🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@breadcrumb-sh/core

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@breadcrumb-sh/core

Embeddable LLM tracing for TypeScript apps. Your database, your deployment, your UI.

latest
Source
npmnpm
Version
0.2.0
Version published
Maintainers
1
Created
Source

@breadcrumb-sh/core

Embeddable LLM tracing for TypeScript apps. Your database, your deployment, your UI.

Breadcrumb captures your LLM calls (tokens, model, cost, cached and reasoning tokens), nests spans into traces, and writes them to your own SQLite or Postgres. You mount a fetch-native handler into your app and render the dashboard from @breadcrumb-sh/react, or build your own UI on the headless API here. Built on OpenTelemetry, with native support for the Vercel AI SDK.

Install

npm i @breadcrumb-sh/core pg              # Postgres
npm i @breadcrumb-sh/core better-sqlite3  # SQLite

pg and better-sqlite3 are optional peer dependencies. Install only the driver for the database you use.

Setup

Create one instance and mount its handler:

// lib/breadcrumb.ts
import { breadcrumb } from "@breadcrumb-sh/core";
import { postgres } from "@breadcrumb-sh/core/adapters";

export const bc = breadcrumb({
  database: postgres(process.env.DATABASE_URL!),
  basePath: "/api/breadcrumb",
  authorize: (req) => isAdmin(req),
  pricing: { "gpt-5": { input: 1.25, output: 10, cachedInput: 0.125 } },
});

Mount bc.handler (a (request: Request) => Promise<Response>) at basePath. It serves the JSON API, not a UI — the dashboard is a separate route rendering <BreadcrumbDashboard>. Framework bridges are provided:

// Next.js: app/api/breadcrumb/[...path]/route.ts
import { toNextHandler } from "@breadcrumb-sh/core/next";
export const { GET, POST, DELETE } = toNextHandler(bc);

// Node / Express
import { toNodeHandler } from "@breadcrumb-sh/core/node";
app.use("/api/breadcrumb", toNodeHandler(bc));

// Anything fetch-native (Hono, SvelteKit, …)
app.all("/api/breadcrumb/*", (c) => bc.handler(c.req.raw));

The schema is created automatically in development (migrations: "auto"). For production, generate migration files with the CLI and set migrations: "manual".

Instrumenting calls

Vercel AI SDK. bc.telemetry() returns settings for experimental_telemetry. Calls made inside a bc.trace() callback nest into the same trace automatically:

import { generateText } from "ai";

const { text } = await generateText({
  model: openai("gpt-5"),
  prompt,
  experimental_telemetry: bc.telemetry({ functionId: "generate-answer", userId, sessionId }),
});

functionId names the call and carries the cost attribution, wherever the call sits in the trace; userId and sessionId group runs by user and conversation.

Your own OpenTelemetry setup. If the app already has a tracer provider (@vercel/otel, NodeSDK, Sentry), register bc.spanProcessor on it and model spans reach breadcrumb without threading bc.telemetry() through every call:

registerOTel({ serviceName: "app", spanProcessors: [bc.spanProcessor] });

It stores only spans breadcrumb can read (ai.*, gen_ai.*, breadcrumb.*) unless shouldExport says otherwise, so a shared provider's HTTP and filesystem spans don't land in the trace table.

Manual tracing. bc.trace(name, attrs?, fn), with nested t.span(...):

await bc.trace("support-reply", { userId }, async (t) => {
  t.set({ input: prompt });
  const docs = await t.span("retrieve", { kind: "retrieval" }, async (s) => {
    const result = await search(prompt);
    s.set({ output: result });
    return result;
  });
  const answer = await callModel(prompt, docs);
  t.set({ output: answer, model: "gpt-5", inputTokens, outputTokens });
});

t.set() accepts model, provider, input/output, token counts (inputTokens, outputTokens, cachedInputTokens, cacheWriteTokens, reasoningTokens), an explicit cost, and metadata. A thrown error marks the span failed and rethrows.

On serverless or edge, call await bc.flush() (or waitUntil(bc.flush())) before the response returns so no spans are lost. Set flushMode: "sync" for those runtimes.

Entry points

ImportExports
@breadcrumb-sh/corebreadcrumb(), the Breadcrumb type, migration helpers (planMigration, renderMigrationSql, EMPTY_SCHEMA_STATE), and the full domain type contract.
@breadcrumb-sh/core/adapterssqlite(fileOrDb), postgres(connectionOrClient).
@breadcrumb-sh/core/clientcreateBreadcrumbClient(), a typed browser fetch client mirroring bc.api.
@breadcrumb-sh/core/kitHeadless UI helpers: traceModel, flowRows, selfTime, hotspots, asMessages, preview, and formatters (fmtCost, fmtTokens, fmtMs, …).
@breadcrumb-sh/core/nodetoNodeHandler() for Node/Express.
@breadcrumb-sh/core/nexttoNextHandler() for the Next.js App Router.

Building your own dashboard

The server, the browser client, and the React hooks share one contract. Query the server directly from a React Server Component with bc.api (listTraces, listSessions, getTrace, stats, costSummary, …), use the typed client in the browser, or reach for @breadcrumb-sh/react hooks. Render with the headless kit:

import { traceModel, selfTime, asMessages, fmtCost } from "@breadcrumb-sh/core/kit";

const model = traceModel(spans);      // rows, scales, hotspots, totals
model.rows;                            // denoised, depth-indexed, ready to map
model.spots;                           // { errorId, slowestId, costliestId }
selfTime(span, children);              // extent minus what the children covered
const chat = asMessages(span.input);   // parse chat-shaped payloads
fmtCost(0.0042);                       // "$0.0042"

traceModel is what the shipped waterfall renders from, so a UI you build from scratch reads exactly the same numbers rather than reimplementing them.

Configuration

Key breadcrumb() options:

OptionDefaultPurpose
databaserequiredA sqlite() or postgres() adapter.
basePath/breadcrumbWhere the handler is mounted, and what the dashboard's api prop points at.
environmentVERCEL_ENV ?? NODE_ENV ?? developmentStamped on every span.
authorizenoneGuards the query routes. Your dashboard page is yours to guard.
ingestnone{ apiKey } enables HTTP ingest endpoints.
pricingnoneUSD per 1M tokens, keyed by model, for cost.
retention90dPer-environment retention windows.
redactnoneScrub or trim each span before storage.
maxPayloadChars16384Truncate captured input/output (0 disables).
flushModebatchsync for serverless/edge.
migrationsautomanual runs no runtime DDL.

Breadcrumb ships no default prices. Omit pricing and only costs you set yourself are stored. See the full reference at breadcrumb.sh/docs/configuration.

License

MIT

FAQs

Package last updated on 28 Jul 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts