@brizz/sdk
v0.1.44
Published
OpenTelemetry-based observability SDK for AI applications
Readme
Brizz SDK
OpenTelemetry-based observability SDK for AI applications. Traces popular AI libraries including OpenAI, Anthropic, Vercel AI SDK, and more.
Table of Contents
- Features
- Installation
- Quick Start
- Module System Support
- Supported Libraries
- PII Masking
- Session Tracking
- Subagents
- Mute Messages
- Multiple Services in One Process
- Custom Events & Logging
- Environment Variables
- Disable Span Export
- Dropping Spans
- Advanced Configuration
- Testing & Development
- Package.json Examples
- Examples
- Troubleshooting
- Contributing
- License
Features
- 🔍 One-Call Setup - Pass your AI libraries to
Brizz.initialize()and they're traced - 📊 OpenTelemetry Native - Standards-compliant tracing, metrics, and logs
- 🛡️ PII Masking - Optional masking for sensitive data
- 🔄 Session Tracking - Group related operations and traces
- 📦 Runs Anywhere - ESM, CommonJS, bundlers, and
tsx
Installation
npm install @brizz/sdk
# or
yarn add @brizz/sdk
# or
pnpm add @brizz/sdkQuick Start
First, set up your environment variables:
BRIZZ_API_KEY=your-api-key
BRIZZ_BASE_URL=https://telemetry.brizz.dev # Optional
BRIZZ_APP_NAME=my-app # Optional
BRIZZ_LOG_LEVEL=info # Optional: debug, info, warn, error, noneThen initialize the SDK in your application, passing the libraries you want to trace:
import { Brizz } from '@brizz/sdk';
import OpenAI from 'openai';
Brizz.initialize({
apiKey: process.env.BRIZZ_API_KEY,
appName: 'my-ai-app',
instrumentModules: { openAI: OpenAI },
});Run your app the way you already do — node app.js, tsx app.ts, or your own start script.
Important: Call
Brizz.initialize()before making any AI calls. If usingdotenv, useimport "dotenv/config"before importing@brizz/sdk.
Module System Support
The same setup works in ESM, CommonJS, bundlers, and tsx — import @brizz/sdk and call
Brizz.initialize().
Bundlers that inline modules (Next.js, Webpack) may need a dynamic import so you can hand the
resolved module to instrumentModules:
// For problematic bundlers
const { default: OpenAI } = await import('openai');
Brizz.initialize({
apiKey: 'your-api-key',
instrumentModules: {
openAI: OpenAI,
},
});Supported Libraries
Pass the libraries you use to instrumentModules and the SDK traces them:
- OpenAI -
openaipackage - Anthropic -
@anthropic-ai/sdkpackage - Vercel AI SDK -
aipackage (generateText, streamText, etc.)- Captured through
experimental_telemetry: { isEnabled: true }on each call, notinstrumentModules
- Captured through
- LangChain -
langchainand@langchain/*packages - LlamaIndex -
llamaindexpackage - AWS Bedrock -
@aws-sdk/client-bedrock-runtime - Vector Databases - Pinecone, Qdrant, ChromaDB
Optional Instrumentations
A few instrumentations ship as optional peer dependencies so they're only pulled in when you need them. Install the matching package(s) to enable them:
| To trace | Install |
| --- | --- |
| Vercel AI SDK v7+ (ai@7) — required for span capture | npm install @ai-sdk/otel |
| Google GenAI / Gemini (@google/genai) — see Gemini | npm install @traceloop/instrumentation-google-generativeai |
| Google Vertex AI (@google-cloud/vertexai) | npm install @traceloop/instrumentation-vertexai |
| MCP (Model Context Protocol) | npm install @arizeai/openinference-instrumentation-mcp @modelcontextprotocol/sdk |
If a package isn't installed, the SDK keeps working and simply skips that
instrumentation. On the automatic path the skip is a quiet debug log; when you
opt in explicitly via instrumentModules, a missing package is logged at error
so the misconfiguration is visible.
Vercel AI SDK Integration
On ai@7 and newer, also install @ai-sdk/otel — v7 moved AI SDK span collection
into that package, so without it your generateText / streamText calls produce no
spans. It isn't needed on ai@6.
For Vercel AI SDK instrumentation, you need to enable telemetry in your function calls:
import { generateText, streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
// For generateText
const result = await generateText({
model: openai('gpt-4'),
prompt: 'Hello, world!',
experimental_telemetry: { isEnabled: true }, // Required for instrumentation
});
// For streamText
const stream = streamText({
model: openai('gpt-4'),
messages: [{ role: 'user', content: 'Hello!' }],
experimental_telemetry: { isEnabled: true }, // Required for instrumentation
});This enables automatic tracing of:
- Model calls and responses
- Token usage and costs
- Tool calls and executions
- Streaming data
Vercel eve
Vercel eve agents emit standard OpenTelemetry GenAI spans, so Brizz ingests
their traces, message content, and cost as soon as you point eve's exporter at your Brizz endpoint.
@brizz/sdk/eve gives you a one-line registerOTel config that reads BRIZZ_DSN — it wires the
exporter and the session grouping that makes each agent run render as a single conversation:
// agent/instrumentation.ts
import { defineInstrumentation } from 'eve/instrumentation';
import { registerOTel } from '@vercel/otel';
import { createEveOtelConfig } from '@brizz/sdk/eve';
export default defineInstrumentation({
setup: ({ agentName }) => registerOTel(createEveOtelConfig({ serviceName: agentName })),
});Then set your DSN in the environment — a single pasted credential:
BRIZZ_DSN=https://<bearer>@<gateway-host>/<service-name>createEveOtelConfig derives the bearer and endpoint from the DSN and ignores its service segment —
eve's agentName stays the service name. Prefer to compose it yourself? The pieces are exported too:
import { createEveTraceExporter, createEveSessionProcessor } from '@brizz/sdk/eve';
registerOTel({
serviceName: agentName,
traceExporter: createEveTraceExporter(),
// 'auto' keeps the default exporter processor.
spanProcessors: [createEveSessionProcessor(), 'auto'],
});@brizz/sdk/eve is a standalone, side-effect-free export: it does not initialize the full SDK, so
it won't duplicate the spans eve already emits.
Google GenAI (Gemini)
@google/genai is Google's unified SDK for Gemini
(both the Gemini Developer API and Vertex AI). Install the peer dependency, then build your client
from the class instrumentGoogleGenAI returns:
import { Brizz } from '@brizz/sdk';
import { instrumentGoogleGenAI } from '@brizz/sdk/google-genai';
import * as genai from '@google/genai';
Brizz.initialize({ appName: 'my-app' });
// Call after Brizz.initialize(). The returned class is instrumented — build the client from it.
const GoogleGenAI = instrumentGoogleGenAI(genai);
const ai = new GoogleGenAI({ vertexai: true, project, location });
const response = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: 'Hello, world!',
});This traces generateContent / generateContentStream — messages, tool calls, token usage, and
cost — with no per-call instrumentation.
Unlike the other integrations, @google/genai is not wired through instrumentModules.
It's an ESM-only package, and a Node ES module namespace is read-only, so it can't be patched in
place — instrumentGoogleGenAI returns an instrumented GoogleGenAI class instead. Construct
your client from that class and you're done.
PII Masking
Optional masking for span attributes.
// Enable default masking
Brizz.initialize({
apiKey: 'your-api-key',
masking: true,
});
// Custom masking configuration
Brizz.initialize({
apiKey: 'your-api-key',
masking: {
spanMasking: {
rules: [
{
attributePattern: 'gen_ai\\.(prompt|completion)',
mode: 'partial', // 'partial' or 'full'
patterns: ['sk-[a-zA-Z0-9]{48}'],
},
],
},
},
});When enabled, defaults cover a curated set of common secret patterns. Add custom rules for anything else you need masked.
Session Tracking
Group related operations under a session context:
Session Context Manager (Recommended)
The startSession function creates a session span and provides a Session object:
import { startSession } from '@brizz/sdk';
// Basic usage - all LLM calls within the callback are automatically linked
const result = await startSession(
'session-123',
async (session) => {
// Add custom properties (optional)
session.updateProperties({ userId: 'user-456', model: 'gpt-4' });
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: userQuery }],
});
return response;
},
{ feature: 'chat' }, // optional: extraProperties propagated to all spans
);Session Methods:
session.updateProperties({ key: value })- Add custom properties to the session spansession.setInput(text, context?)- (Optional) Manually track input text; optional context bag attaches per-turn metadata rendered in the dashboard's Context panelsession.setOutput(text, context?)- (Optional) Manually track output text; optional context bag attaches per-turn metadata rendered in the dashboard's Context panelsession.setTitle(text)- Set a session title (typically used withmode: 'title')session.addExternalLink(url, options?)- (Optional) Attach an external link (e.g. a Datadog trace or dashboard) to the session; it appears on the session detail panel. Also available as the top-leveladdExternalLink(url, options?).
Per-turn context example:
await startSession('session-123', async (session) => {
session.setInput('Why is my bill high?', { selected_invoice: 'INV-9182' });
const response = await openai.chat.completions.create({ ... });
session.setOutput(response.choices[0].message.content, {
message_id: 'msg-42',
sources: ['doc-abc'],
});
});When to use manual input/output tracking:
In most cases, Brizz automatically captures inputs and outputs from your LLM calls. Use
setInput/setOutput for special scenarios:
- Multi-agent flows: Track only user-facing input/output, not intermediate agent communications
- Structured data extraction: Track a specific field from complex JSON requests
- Post-processing: Track transformed responses before returning to the user
await startSession('session-456', async (session) => {
// Extract just the query from a structured request
const requestData = { query: "What's the weather?", context: {...} };
session.setInput(requestData.query);
const response = await openai.chat.completions.create({...});
// Extract answer from structured response
const responseJson = JSON.parse(response.choices[0].message.content);
session.setOutput(responseJson.answer);
return responseJson;
});External link example:
import { addExternalLink, startSession } from '@brizz/sdk';
startSession('session-123', (session) => {
// Top-level function — resolves the active session from context.
addExternalLink('https://app.datadoghq.com/trace/abc', { title: 'Datadog trace' });
});
// Outside a session — pass the id explicitly.
addExternalLink('https://sentry.io/issues/456', { sessionId: 'session-123', linkType: 'sentry' });Session Title Generation
If you use an LLM call to generate session titles, wrap it so those spans don't appear as part of the conversation:
import { startSession, startSessionTitle } from '@brizz/sdk';
await startSession('session-123', async (session) => {
const response = await openai.chat.completions.create({...});
// Title generation — excluded from conversation view
await startSessionTitle(async (title) => {
const t = await openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: 'Summarize this chat in 3 words' }],
});
title.setTitle(t.choices[0].message.content);
});
});
// Or use mode: 'title' on startSession directly
await startSession('session-123', async (session) => {
const t = await openai.chat.completions.create({...});
session.setTitle(t.choices[0].message.content);
}, undefined, { mode: 'title' });
// Or use startSessionTitle outside a session (pass sessionId explicitly)
await startSessionTitle(async (title) => {
title.setTitle('My Title');
}, { sessionId: 'session-123' });Accessing the Active Session
Use getActiveSession() to retrieve the current session from anywhere within a startSession scope
— no need to pass the session object through your call stack:
import { startSession, getActiveSession } from '@brizz/sdk';
function deepHelper() {
const session = getActiveSession();
session?.updateProperties({ step: 'helper' });
}
await startSession('session-123', async () => {
deepHelper(); // accesses session without it being passed as a parameter
});
// Outside a session, returns undefined
getActiveSession(); // undefinedFunction Wrapper Pattern
For simpler cases where you just need to tag traces with a session ID:
import { withSessionId, emitEvent } from '@brizz/sdk';
async function processUserWorkflow(userId: string) {
// All traces within this function will include the session ID
const result = await generateText({
model: openai('gpt-4'),
messages: [{ role: 'user', content: 'Hello' }],
experimental_telemetry: { isEnabled: true },
});
return result;
}
// Create a wrapped function that always executes with session context
// withSessionId(sessionId, fn, thisArg?, extraProperties?)
const sessionedWorkflow = withSessionId('session-123', processUserWorkflow, undefined, {
feature: 'workflow',
});
// Call multiple times, each with the same session context
await sessionedWorkflow('user-456');
await sessionedWorkflow('user-789');Immediate Execution Pattern
import { callWithSessionId } from '@brizz/sdk';
// Execute function immediately with session context
await callWithSessionId('session-123', processUserWorkflow, null, 'user-456');Identifying Users, Organizations & Messages
Attach the end-user, their organization, and a per-message id with typed setters. Call them inside a session — they apply to the turn's spans:
import { startSession, setUser, setOrganization, setMessageId } from '@brizz/sdk';
await startSession(sessionId, async () => {
setUser({ id: user.id, email: user.email, role: user.role, plan: user.plan });
setOrganization({ id: org.id, name: org.name, plan: org.plan, domain: org.domain });
setMessageId(message.id); // your own id, to reference this message later
return agent.run(prompt);
});Only id is required. Each field maps to its own attribute (brizz.user.id, brizz.organization.plan, brizz.message.id, …). For anything beyond the named fields, pass a traits record — each entry becomes brizz.user.<key> / brizz.organization.<key>:
setUser({ id: user.id, traits: { department: 'sales', signup_source: 'referral' } });
setOrganization({ id: org.id, traits: { industry: 'fintech' } });The same setters are available on the session object (session.setUser(...)) and as scoped wrappers (callWithUser / withUser).
Recording Feedback
Capture an end-user's reaction to a specific reply — a 👍/👎, a rating, a reason. Pair it with the message id you set on the turn:
import { startSession, setMessageId, recordFeedback } from '@brizz/sdk';
await startSession(sessionId, async () => {
setMessageId(message.id); // the id you'll reference this reply by
const reply = await agent.run(prompt);
recordFeedback('thumbs_up'); // defaults to the current message
});Only type is required; score, reason, comment, and source are optional, and attributes adds free-form brizz.feedback.<key> entries. Feedback is anchored by messageId and/or sessionId, so you can send it later — even minutes or days after the reply — by passing the id(s) explicitly:
recordFeedback('thumbs_down', { messageId: message.id, sessionId, reason: 'inaccurate' });Recording Metrics
Report a number your own system already computes about an interaction — an eval score, a customer rating, a latency, a cost. It becomes a real Brizz metric you can filter and chart by, rather than an untyped bag of event attributes.
import { startSession, recordMetric } from '@brizz/sdk';
await startSession(sessionId, async () => {
const reply = await agent.run(prompt);
const score = await myEvaluator.score(prompt, reply);
recordMetric({
name: 'quality_score',
value: score,
unit: 'score',
minValue: 0,
maxValue: 1,
polarity: 'positive',
});
});polarity tells Brizz which direction is good — a rising quality_score is an improvement, a rising hallucination_rate is a regression. minValue / maxValue describe the scale, so a 4 out of 5 isn't read as a 4 out of 1.
Scoring often happens after the fact. Pass sessionId to attach a metric from anywhere, and timestamp to say when the measured thing happened, so a nightly job lands the score on the turn it describes rather than on the evaluation run:
recordMetric({
name: 'quality_score',
value: await judge.score(session),
sessionId: session.id,
timestamp: session.endedAt,
comment: 'graded by the nightly LLM judge',
attributes: { evaluator: 'gpt-4o', rubric: 'v2' },
});attributes are flat labels you can slice the metric by. Re-reporting the same metric supersedes the previous value, so a re-score wins over the original.
Custom Properties
Add custom properties to telemetry context:
import { withProperties, callWithProperties } from '@brizz/sdk';
// Wrapper pattern - properties applied to all calls
const taggedFn = withProperties({ env: 'prod', feature: 'chat' }, myFunction);
await taggedFn(args);
// Immediate execution - properties applied once
await callWithProperties({ userId: 'user-123' }, myFunction, null, args);Handling Method Context
When wrapping methods that use this, you have several options:
Option 1: Arrow Function (Recommended)
class ChatService {
async processMessage(userId: string, message: string) {
// This method uses 'this' context
return `Processed by ${this.serviceName}: ${message}`;
}
}
const service = new ChatService();
// Wrap with arrow function to preserve 'this' context
const sessionedProcess = withSessionId('session-123', (userId: string, message: string) =>
service.processMessage(userId, message),
);Option 2: Using bind()
// Pre-bind the method to preserve 'this' context
const sessionedProcess = withSessionId('session-123', service.processMessage.bind(service));Option 3: Explicit thisArg Parameter
// Pass 'this' context explicitly as third parameter
const sessionedProcess = withSessionId(
'session-123',
service.processMessage,
service, // explicit 'this' context
);Note: The arrow function approach (Option 1) is recommended as it's more explicit, avoids lint
warnings, and is less prone to this binding issues.
Subagents
Mark work as a subagent so it shows as its own lane in the conversation instead of mixing into the session's main transcript. Nesting composes, and two runs of the same agent stay two lanes.
Three forms, same first argument — a name, { name, id, parentId }, or { token }:
import { setAgent, startAgent, withAgent } from '@brizz/sdk';
// startAgent — scoped: runs the callback now, returns its return value
await startAgent('research', () => researchAgent.generate({ prompt: task }));
// withAgent — wraps a function; every call is its own run, so its own lane
const research = withAgent('research', runResearch);
await research(topicA);
await research(topicB);
// setAgent — imperative, no scope: marks everything created after it, and
// returns the handle. The natural form at the top of a job or route handler.
export async function handleJob(job) {
setAgent({ token: job.brizzAgent });
await boardCreator.generate({ prompt: job.goal });
}
const { token } = setAgent('board-creator'); // grab the token to send onwardsetAgent mirrors setUser / setSessionId — there is no auto-reset, so use startAgent when you want the scope to close.
Handing a subagent to code running elsewhere — a queue message, an RPC argument, a database row — is the token: send it with the work and re-enter the agent from it on the other side. It carries the session and trace context, so the detached run joins the same conversation.
await startAgent('board-creator', ({ token }) => sendToWorker({ goal, brizzAgent: token }));Already tracking your own agent ids? Pass them instead of a bare name:
await startAgent({ name: 'research', id: runId, parentId: callerRunId }, () => run());id is your id for this run (unique per run, or two runs share a lane) and parentId overrides the enclosing agent. Anything omitted is filled in — a fresh id per run, the enclosing agent as the parent. Don't give withAgent an id: it would put every call to the wrapped function in one lane.
Mute Messages
Keep internal or unrelated LLM calls — summarization, title generation, classification, guardrail checks — out of the captured conversation. The call still runs and its telemetry (latency, tokens, cost) is recorded; only the content is left out — the prompt, the reply, and the tool calls.
import { callWithMute, withMute } from '@brizz/sdk';
// Hide both sides of an internal call
await callWithMute({}, () => agent.run('Summarize this conversation for internal logging.'));
// Keep the assistant reply, drop the prompt
await callWithMute({ output: false }, () => agent.run('…a long internal prompt…'));
// Keep the tool calls, drop the prompt and the reply
await callWithMute({ tools: false }, () => agent.run('…a long internal prompt…'));
// Pre-wrap a function to reuse the same muting
const muteTitle = withMute({}, agent.run, agent);tools covers tool calls and their results. Leave it out and it follows output.
Deployment Environment
Optionally specify the deployment environment for better filtering and organization:
Brizz.initialize({
apiKey: 'your-api-key',
appName: 'my-app',
environment: 'production', // Optional: 'dev', 'staging', 'production', etc.
});Multiple Services in One Process
appName names the whole process. When one process runs several agents that
should show up as separate services in Brizz, scope the service name instead:
import { callWithServiceName, withServiceName, setServiceName } from '@brizz/sdk';
// Everything traced inside the callback reports as 'checkout-agent',
// including auto-instrumented LLM calls
await callWithServiceName('checkout-agent', async () => {
return openai.chat.completions.create({ ... });
});
// Pre-wrap a handler
const handleSupport = withServiceName('support-agent', supportHandler);
// Imperative form, for when the enclosing span already exists
// (an MCP or HTTP instrumentation opens its span before your handler runs)
setServiceName('billing-agent');Spans and events created outside any of these keep the configured appName.
Custom Events & Logging
Emit custom events and structured logs:
import { emitEvent, emitEventWithSessionId, logger } from '@brizz/sdk';
// Emit custom events
emitEvent('user.signup', {
userId: '123',
plan: 'pro',
source: 'website',
});
emitEvent('ai.request.completed', {
model: 'gpt-4',
tokens: 150,
latency: 1200,
});
// Emit with a known session ID, outside any session scope
emitEventWithSessionId('session-123', 'user.feedback.submitted', {
rating: 5,
});
// Structured logging
logger.info('Processing user request', { userId: '123', requestId: 'req-456' });
logger.error('AI request failed', { error: err.message, model: 'gpt-4' });Environment Variables
# Required
BRIZZ_API_KEY=your-api-key
# Optional Configuration
BRIZZ_BASE_URL=https://telemetry.brizz.dev # Default telemetry endpoint
BRIZZ_APP_NAME=my-app # Application name
BRIZZ_APP_VERSION=1.0.0 # Application version
BRIZZ_ENVIRONMENT=production # Environment (dev, staging, prod)
BRIZZ_LOG_LEVEL=info # SDK log level
BRIZZ_DISABLE_SPAN_EXPORTER=true # Skip span export (see "Disable Span Export")
# OpenTelemetry Standard Variables
OTEL_SERVICE_NAME=my-service # Service name
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true # Capture AI contentDisable Span Export
Keep Brizz.initialize() in your code without sending any spans — useful for dev/test environments.
When enabled, the SDK skips span exporter and processor setup, so no spans are exported. Metrics and
logs continue to work.
Brizz.initialize({ apiKey: 'your-api-key', disableSpanExporter: true });Or via env var: BRIZZ_DISABLE_SPAN_EXPORTER=true.
Dropping Spans
Filter spans before export with beforeSendSpan. Return false to drop a span, true to keep it. Useful for stripping noisy paths (health checks, internal tooling) or excluding telemetry for specific end-users.
Brizz.initialize({
apiKey: 'your-api-key',
beforeSendSpan: (span) => {
// OpenInference (LangChain JS, LangGraph JS) packs per-call
// `config.metadata` into a JSON-stringified `metadata` attribute.
const raw = span.attributes['metadata'];
if (typeof raw !== 'string') return true;
try {
return JSON.parse(raw).customer_id !== 'cust_42';
} catch {
return true;
}
},
});Tag the call so the filter has something to match on:
await llm.invoke([new HumanMessage('Hello')], {
metadata: { customer_id: 'cust_42' },
});Both sync and async filters are supported. Exceptions are caught and the span passes through.
Advanced Configuration
Brizz.initialize({
apiKey: 'your-api-key',
appName: 'my-app',
baseUrl: 'https://telemetry.brizz.dev',
// Custom headers for authentication
headers: {
'X-API-Version': '2024-01',
'X-Environment': 'production',
},
// Disable batching for immediate export (testing)
disableBatch: false,
// Custom exporters for testing
customSpanExporter: new InMemorySpanExporter(),
customLogExporter: new InMemoryLogExporter(),
// Disable internal NodeSDK (advanced usage)
disableNodeSdk: false,
// Log level for SDK diagnostics
logLevel: 'info', // debug, info, warn, error, none
});Testing & Development
For testing and development, you can use in-memory exporters:
import { InMemorySpanExporter } from '@opentelemetry/sdk-trace-base';
const spanExporter = new InMemorySpanExporter();
Brizz.initialize({
apiKey: 'test-key',
customSpanExporter: spanExporter,
logLevel: 'debug',
});
// Later in tests
const spans = spanExporter.getFinishedSpans();
expect(spans).toHaveLength(1);
expect(spans[0].name).toBe('openai.chat');Package.json Examples
The SDK is initialized from your application code, so your scripts stay as they are:
{
"scripts": {
"start": "node src/index.js",
"dev": "node --watch src/index.js",
"debug": "node --inspect src/index.js"
}
}Examples
Check out the examples directory for complete working examples:
- Basic Usage - Simple AI application setup
- Vercel AI SDK - Integration with Vercel's AI SDK
- Session Tracking - Grouping related operations
- Custom Events - Emitting business metrics
- PII Masking - Data protection configuration
Troubleshooting
Common Issues
"Could not find declaration file"
- Make sure to build the SDK:
pnpm build - Check that
dist/contains.d.tsfiles
Instrumentation not working
- Ensure
Brizz.initialize()runs before any AI calls - Pass the libraries you want to trace via
instrumentModules - Check that
BRIZZ_API_KEYis set - For Vercel AI SDK: Add
experimental_telemetry: { isEnabled: true }to function calls
CJS/ESM compatibility issues
- Use dynamic imports in CommonJS:
const ai = await import('ai') - For bundlers, use manual instrumentation with
instrumentModules
Debug Mode
Enable debug logging to troubleshoot issues:
BRIZZ_LOG_LEVEL=debug node your-app.jsContributing
See our Contributing Guide for development setup and guidelines.
License
Apache-2.0 - see LICENSE file.
