@superpenguin/js
v0.7.1
Published
SuperPenguin JavaScript SDK for AI cost attribution across native provider SDKs, Vercel AI SDK, and OpenTelemetry
Readme
SuperPenguin JavaScript SDK
TypeScript SDK for request-level AI cost attribution. It wraps native provider
SDKs for OpenAI, Anthropic, Google Gemini, AWS Bedrock, Deepgram, and
ElevenLabs, and still supports Vercel AI SDK helpers plus OpenTelemetry span
ingestion. Use it in server code only. Do not expose SP_API_KEY to browsers.
Install
npm install @superpenguin/js
npm install openai @anthropic-ai/sdk @google/genai @deepgram/sdk @elevenlabs/elevenlabs-jsInstall only the provider package(s) you actually use. Provider SDKs are
optional peer dependencies so @superpenguin/js does not force all of them into
your app.
Native Provider SDKs
import OpenAI from "openai";
import { init, wrap } from "@superpenguin/js";
init({ apiKey: process.env.SP_API_KEY });
const openai = wrap(new OpenAI(), {
metadata: {
environment: "production",
feature: "support_agent",
},
});
await openai.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: "Write a concise reply." }],
spMetadata: {
customer_id: "cust_acme_123",
},
});For AWS Bedrock, wrap a BedrockRuntimeClient (AWS SDK v3). The client uses a
command-dispatch surface, so wrap() patches client.send and tracks
Converse / ConverseStream commands:
import { BedrockRuntimeClient, ConverseCommand } from "@aws-sdk/client-bedrock-runtime";
import { init, wrap } from "@superpenguin/js";
init({ apiKey: process.env.SP_API_KEY });
const bedrock = wrap(new BedrockRuntimeClient({ region: "us-east-1" }), {
metadata: { environment: "production", feature: "support_agent" },
});
await bedrock.send(
new ConverseCommand({
modelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
messages: [{ role: "user", content: [{ text: "Write a concise reply." }] }],
}),
);wrap() detects supported clients automatically. Provider-specific exports are
also available: wrapOpenAI, wrapAnthropic, wrapGoogleGenAI,
wrapBedrock, wrapDeepgram, and wrapElevenLabs.
Supported methods in 0.5.0:
- OpenAI:
chat.completions.create,completions.create,embeddings.create,responses.create, including async iterable streams where the SDK returns usage. - Anthropic:
messages.createandmessages.stream().finalMessage(). - Google GenAI:
models.generateContentandmodels.generateContentStream. - AWS Bedrock:
BedrockRuntimeClient.send()forConverseCommandandConverseStreamCommand(other commands pass straight through). Bedrock is priced server-side against SuperPenguin'saws_bedrockrate card. - Deepgram:
listen.v1.media.transcribeUrlandtranscribeFile. - ElevenLabs:
textToSpeech.convert,textToSpeech.stream, plus sound effectsconvert/streamwhen present.
Use spMetadata or sp_metadata on a request for per-call attribution. The
wrapper removes those SuperPenguin-only fields before forwarding the provider
request. The provider endpoint host is also captured automatically as base_url
(handy for gateways/proxies).
Metadata fields
Recognized first-class attribution keys (anything else is stored as custom tags):
| Field | Purpose |
|-------|---------|
| customer_id | End-customer or account consuming the call |
| feature | Product feature name |
| team | Internal team owning the feature |
| environment | production, staging, dev, etc. |
| prompt_key | Identifier for the prompt template |
| prompt_version | Version of the prompt template |
| session_id | Groups one conversation (used by content capture) |
| turn_id | Groups one turn, which may span multiple model calls (used by content capture) |
Batch APIs
Provider Batch APIs return results asynchronously, so there is no live call to
wrap. After a batch job completes, pass the retrieved batch to the matching
tracker and one attribution row per request is recorded with batch=true:
import OpenAI from "openai";
import { init, trackOpenAIBatch } from "@superpenguin/js";
init({ apiKey: process.env.SP_API_KEY });
const openai = new OpenAI();
const batch = await openai.batches.retrieve("batch_abc");
const count = await trackOpenAIBatch(openai, batch, {
metadata: { feature: "nightly-eval" },
});
// Also: trackAnthropicBatch(client, batch, ...) and trackGoogleBatch(...)Content capture (opt-in)
Off by default. The SDK sends only cost metadata, never prompt or completion text, unless your organization opts in from Settings (Pro or Enterprise). Once enabled, a sampled subset of prompts and completions is captured automatically, encrypted at rest, text-only (images/audio/binary stripped), and redacted by default.
Group multi-turn conversations with session_id (one conversation) and
turn_id (one turn). Each model call is its own captured row, so a turn with a
tool call (model -> tool -> model) spans two rows; reuse the same turn_id
across them to group. If turn_id is omitted, each row gets a fresh random one
and will not group.
await openai.chat.completions.create({
model: "gpt-4o",
messages,
spMetadata: {
session_id: conversationId,
turn_id: turnId, // reuse across a tool round-trip to group calls
customer_id: "cust_acme_123",
},
});configureContentCapture() can only reduce capture (it never enables it,
the Settings opt-in is always required):
import { configureContentCapture } from "@superpenguin/js";
configureContentCapture({
allow: true, // false vetoes ALL capture from this process
recordPrompt: true, // false drops inputs, keeps outputs
recordOutcome: true, // false drops outputs, keeps inputs
sampleRateMultiplier: 1, // scale the server sample rate down (0-1)
});Vercel AI SDK
import { init, trackGenerateText } from "@superpenguin/js";
import { generateText } from "ai";
init({ apiKey: process.env.SP_API_KEY });
const trackedGenerateText = trackGenerateText(generateText, {
provider: "vercel_ai_gateway",
functionId: "support-reply",
metadata: {
environment: "production",
feature: "support_agent",
},
});
export async function POST(request: Request) {
const { prompt, customerId } = await request.json();
const result = await trackedGenerateText({
model: "xai/grok-4.1-fast-non-reasoning",
prompt,
metadata: {
customer_id: customerId,
},
telemetry: {
functionId: "support-reply",
},
});
return Response.json({ text: result.text });
}For Vercel AI Gateway calls, set AI_GATEWAY_API_KEY where the Vercel AI SDK
runs. Set SP_API_KEY alongside it so SuperPenguin can ingest attribution rows.
Streaming
import { init, trackStreamText } from "@superpenguin/js";
import { streamText } from "ai";
init({ apiKey: process.env.SP_API_KEY });
const trackedStreamText = trackStreamText(streamText, {
provider: "vercel_ai_gateway",
functionId: "chat-stream",
});
const result = trackedStreamText({
model: "xai/grok-4.1-fast-non-reasoning",
messages,
metadata: {
customer_id: "cust_acme_123",
feature: "chat",
},
});The wrapper waits for the Vercel AI SDK usage promise in the background and submits one attribution event when usage is available.
OpenTelemetry
import { submitSpan } from "@superpenguin/js";
await submitSpan(span, {
metadata: {
environment: "production",
},
});Vercel AI SDK telemetry fields such as functionId and OpenTelemetry trace IDs
are stored under custom_tags unless they map to a first-class attribution
column.
Runtime Behavior
- Events submit immediately by default so serverless functions do not exit before attribution is sent.
- Transient ingest failures retry with exponential backoff and requeue.
- Attribution failures do not crash the user request path unless you create a
client with
throwOnError: true. - For long-lived workers, pass
flushMode: "interval"to batch events and callflush()before shutdown.
